⬢ DragonFlyBSD Kernel Audit
DF-3806 / verdict.json
← back to finding ↓ download raw
{
  "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)"
}