DF-3760 / verdict.json
{ "finding_id": "DF-3760", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Mechanism PROVEN on the guest kernel by deterministic replica of if_alc.cs exact callout/serializer protocol; alc-specific runtime hardware-gated (inconclusive per family convention, DF-3715 precedent). alc_tick (sys/dev/netif/alc/if_alc.c:3408-3436) is an MP callout (callout_init_mp :1353) whose callback BLOCKS in lwkt_serialize_enter (:3414) on the driver serializer and self re-arms at hz (:3428); alc_stop tears it down with callout_stop (:3973) while holding that same serializer (ASSERT_SERIALIZED :3965). Callers holding the serializer: alc_init (:3580; reached from alc_ioctl SIOCSIFFLAGS up/down :2824/:2826, watchdog :2774/:2779, alc_intr DMA-error reinit :3087/:3103), alc_detach (:1615-1616), alc_suspend (:2437). 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 (all later driver paths on that interface block), and the affected CPUs softclock stops delivering all callouts. Kernel-module replica on the guest (alc_callout_replica.ko, exact if_alc.c protocol): stop under a held serializer with the alc_tick-shaped handler parked -> callout_stop NEVER returned (dmesg marker is the last line; sysctl thread 1334 D5 wchan costp == kern_timeout.c:923, run.log); fix-shape control (try-enter + 1-tick retry, i.e. this packs fix.diff) -> callout_stop returned, pending=0 (fix mechanism validated live). Negative per DF-3715: on this kern_timeout.c a post-callback STOP unloads the racing re-arm, so the defect is the deadlock, not a re-arm UAF. The alc instance itself needs AR81xx hardware (guest: NO_ATHEROS_PCI, ifconfig=vtnet0 lo0, NO_ALC_KLD, no hw.alc sysctl; if_alc.ko exists only as a module file; guest src md5 == audited tree). Trigger on real hardware: ifconfig alc0 down, MTU change on a running interface, detach/kldunload, suspend/resume, DMA-error reinit - any alc_stop while alc_tick is INPROG-parked; ~0.1-1% race per stop but repeatable by root.", "exploit_chain": "Privileged local user repeatedly flaps the interface (ifconfig alc0 down / change MTU / kldunload if_alc) until a 1 Hz tick callback is dequeued-and-parked during an alc_stop; both threads wedge permanently (unkillable D-state), NIC and all serializer-sharing paths die, CPU-wide callout delivery stops on the wedged softclock thread. No privilege-escalation primitive; impact is permanent system degradation.", "evidence": "run.log (replica session transcript + wedged-thread ps capture 1334 D5 costp sysctl kern.alcreplica.stop_blk=1 + serial-console tail showing the marker as the last line); build.log (module built on guest, clean with -Werror); env.txt (post-reset fresh instance: no Atheros device, module file only, md5 of if_alc.c == audited tree); VERDICT.md (path:line trace); alc_callout_replica.c; fix.diff (try-enter + 1-tick retry)", "kernel_refs": [ "sys/dev/netif/alc/if_alc.c:1353", "sys/dev/netif/alc/if_alc.c:3414", "sys/dev/netif/alc/if_alc.c:3428", "sys/dev/netif/alc/if_alc.c:3965", "sys/dev/netif/alc/if_alc.c:3973", "sys/dev/netif/alc/if_alc.c:3580", "sys/dev/netif/alc/if_alc.c:2826", "sys/dev/netif/alc/if_alc.c:1615", "sys/dev/netif/alc/if_alc.c:3103", "sys/kern/kern_timeout.c:1091", "sys/kern/kern_timeout.c:923" ], "poc_changes": "No seed PoC. Authored alc_callout_replica.ko (out-of-tree kmodule replicating if_alc.cs callout protocol, both the shipped blocking shape and the fix.diff try-based shape) because the guest has no Atheros hardware; the deadlock is in the generic protocol alc uses, so the replica exercises the identical kernel paths. Adapted from the DF-3715 replica with the alc-specific shapes/periods (hz re-arm, single tick callout) and sysctl namespace.", "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": 420, "guest_dirty": 0, "build_cmd": "cd /root/kmod && make", "run_cmd": "vm.sh run_root kldload /root/kmod/alc_callout_replica.ko; sysctl kern.alcreplica.arm=1; sleep 2; sysctl kern.alcreplica.stop_try=1; (sysctl kern.alcreplica.stop_blk=1 >/root/blk.log 2>&1 &); sleep 5; dmesg | grep alcreplica | tail -3 (guest deliberately wedged by the last step; recovered with vm.sh reset with-src)", "code_hash": "e93eefc55db7e174294158977127a0b607784b75f572c291251ad75f40e27a76", "notes": "Same defect class as DF-3715 (if_em.c) - independent instance in if_alc.c, first filed for this file (pass-2 known findings were only DF-1206/DF-1207). 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 alc_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 clean) switches alc_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. Full driver-level validation is not possible on a guest without Atheros hardware.", "fix_evidence": "fix.diff; run.log stop_try section (alcreplica[try]: callout_stop returned, pending=0)" } |