DF-3806 / verdict.json
{ "finding_id": "DF-3806", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Mechanism PROVEN on the guest kernel by deterministic replica of if_msk.cs exact callout/serializer protocol; msk-specific runtime hardware-gated (inconclusive per family convention, DF-3715/3738/3760/3776 precedent). msk_attach arms a NON-MPSAFE callout (callout_init, if_msk.c:1375); msk_tick (if_msk.c:2930-2946) BLOCKS in lwkt_serialize_enter(ifp->if_serializer) (:2936) and self re-arms at hz (:2943); msk_stop tears it down with callout_stop (:3689) while holding that same serializer (ASSERT_SERIALIZED :3687). Callers holding the serializer: msk_init (:3352; from msk_ioctl SIOCSIFFLAGS up/down :880-881/:895 and watchdog reinit :2663/:2686), msk_detach (:1762-1764), mskc_shutdown (:2699-2701), mskc_suspend (:2719-2722), mskc_resume (:2756). DragonFly callout_stop synchronously waits for an in-progress callback (sys/kern/kern_timeout.c:1091 -> _callout_cancel_or_stop sync=1; ssleep costp at :923), so a tick callback dequeued and parked on the serializer deadlocks against the stopper (AB-BA): both threads wedge permanently, the interface serializer is never released, and because the msk callback is NON-MPSAFE the wedged softclock thread also holds the MP lock and stops delivering every callout on its CPU (observed live: an unrelated sleep 6 starved in nanslp). Kernel-module replica on the guest (msk_callout_replica.ko, exact if_msk.c protocol, non-mp callout_init): stop under a held serializer with the msk_tick-shaped handler parked -> callout_stop NEVER returned (ps: 1045 D0 costp sysctl kern.mskrep.stop_blk=1 == kern_timeout.c:923, and softclock 1 B1 slize == parked in lwkt_serialize_enter, wedge.log); fix-shape control (try-enter + 1-tick retry, i.e. this packs fix.diff) -> callout_stop returned, pending=0 (fix mechanism validated live). An earlier tsleep-based orchestration additionally demonstrated the softclock-blackout cascade one step before callout_stop (sysctl stuck D1 blkw, softclock B1 slize). The msk instance itself needs Marvell Yukon hardware (guest: NO_MARVELL_PCI, ifconfig=vtnet0 lo0, if_msk.ko exists only as a module file; guest src md5 == audited tree). Trigger on real hardware: ifconfig mskN down/re-up, detach/kldunload, suspend/resume, watchdog reinit - any msk_stop while msk_tick is INPROG-parked; ~0.1-1% race per stop but repeatable by root.", "exploit_chain": "Privileged local user repeatedly flaps the interface (ifconfig mskN down / kldunload if_msk / suspend-resume) until a 1 Hz non-MPSAFE tick callback is dequeued-and-parked during an msk_stop; both threads wedge permanently (unkillable D-state), both ports serializer is never released, the wedged softclock CPU stops delivering all callouts and holds the MP lock (system-wide timer degradation). No privilege-escalation primitive; impact is permanent system degradation/DoS.", "evidence": "wedge.log (decisive: mskrep[blk]: msk_tick handler BLOCKED ... waited=99 followed by NO return line, ps capture 1045 D0 costp + softclock 1 B1 slize + starved sleep 6 nanslp); run.log (session transcript: fix-shape control mskrep[try]: callout_stop returned, pending=0 twice, plus attempt-1 tsleep-orchestration cascade); build.log; env.txt (no Marvell PCI, src md5 == tree); VERDICT.md (path:line trace); msk_callout_replica.c; fix.diff (try-enter + 1-tick retry)", "kernel_refs": [ "sys/dev/netif/msk/if_msk.c:1375", "sys/dev/netif/msk/if_msk.c:2930", "sys/dev/netif/msk/if_msk.c:2936", "sys/dev/netif/msk/if_msk.c:2943", "sys/dev/netif/msk/if_msk.c:3687", "sys/dev/netif/msk/if_msk.c:3689", "sys/dev/netif/msk/if_msk.c:3352", "sys/dev/netif/msk/if_msk.c:880", "sys/dev/netif/msk/if_msk.c:1762", "sys/dev/netif/msk/if_msk.c:2663", "sys/dev/netif/msk/if_msk.c:2699", "sys/dev/netif/msk/if_msk.c:2719", "sys/dev/netif/msk/if_msk.c:2756", "sys/kern/kern_timeout.c:923", "sys/kern/kern_timeout.c:1091" ], "poc_changes": "No seed PoC. Authored msk_callout_replica.ko (out-of-tree kmodule replicating if_msk.cs callout protocol with the msk-specific shapes: NON-MPSAFE callout_init per :1375, hz re-arm, single tick callout) because the guest has no Marvell hardware; the deadlock is in the generic protocol msk uses, so the replica exercises the identical kernel paths. Adapted from the DF-3760 replica with the msk shapes and the kern.mskrep sysctl namespace. Three orchestration iterations: (1) tsleep-based park detection -> wedge occurred one step early via softclock-timeout starvation (itself evidence of the non-MPSAFE/MP-lock cascade); (2) fixed-iteration spin (300M) shorter than the 1 s tick period -> missed the park, callout_stop returned on a merely-pending callout; (3) FINAL: ticks-based spin (hardclock-immune) + lwkt_user_yield -> park caught (fired=4 ran=3 waited=99), callout_stop wedged with the canonical costp/slize pair.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 1500, "guest_dirty": 0, "build_cmd": "cd /root/kmod3806 && make", "run_cmd": "vm.sh run_root: kldload /root/kmod3806/msk_callout_replica.ko; sysctl kern.mskrep.arm=1; sleep 2; sysctl kern.mskrep.stop_try=1; (sysctl kern.mskrep.stop_blk=1 &); sleep 6; dmesg | grep mskrep[blk] | tail -2; ps -o pid,stat,wchan,command -ax | grep -E stop_blk|softclock (guest deliberately wedged by the last step; recovered with vm.sh reset with-src)", "code_hash": "7a4740a4d781555918b2a4c71b088415ac237169931963ef88d7e6f299577609", "notes": "Same defect class as DF-3715 (if_em.c)/DF-3738 (if_emx.c)/DF-3760 (if_alc.c)/DF-3776 (if_jme.c) - fifth independent instance, first filed for this file (pass 1 marked the file clean; DB confirmed zero pass-1 findings). msks variant is the non-MPSAFE callout shape, adding the MP-lock/softclock-blackout cascade the MP-safe family members do not have - observed live in attempt 1 and in the final run (starved bystander sleep 6). The replicas stop_try control doubles as live validation of the fix.diff mechanism on this kernel. Guest was wedged by design during the positive test; vm.sh reset with-src performed and verified clean (env.txt).", "recommended_fix": "Make msk_tick() non-blocking: lwkt_serialize_try() with a 1-tick callout_reset retry (see fix.diff; DF-3715 fix pattern).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff (git apply --check clean) switches msk_tick to lwkt_serialize_try + 1-tick retry, mirroring the DF-3715 fix for if_em.c. The mechanism of the fix is validated live on the guest: the replicas fix-shape control (identical try-enter + retry protocol) shows callout_stop under the serializer returns with pending=0, no deadlock (run.log, both sessions). Full driver-level validation is not possible on a guest without Marvell Yukon hardware.", "fix_evidence": "fix.diff; run.log/wedge.log stop_try section (mskrep[try]: callout_stop returned, pending=0 / RESULT pending=0)" } |