⬢ DragonFlyBSD Kernel Audit
DF-4316 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4316",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Mechanism PROVEN on the guest kernel by a deterministic replica of if_lge.cs exact callout/serializer protocol; the lge-specific runtime is hardware-gated (no Level-One LXT1001 NIC on the guest: ifconfig -l -> vtnet0 lo0) -> inconclusive per DF-3715 family convention. lge_tick() (if_lge.c:978-987) BLOCKS in lwkt_serialize_enter(ifp->if_serializer) (:984) and re-arms itself INSIDE that lock (:1017); lge_stop() tears the callout down with callout_stop() (:1442) while holding the same serializer -- and every in-tree lge_stop caller holds it (lge_detach :576-580, lge_init :1211 via SIOCSIFFLAGS :1387 / lge_rxeoc :933 / lge_watchdog :1425, lge_intr :1029, lge_ioctl :1391). DragonFly callout_stop() is synchronous (kern_timeout.c:1091 -> sync=1): when the callback is INPROG the caller ssleep(costp)s (kern_timeout.c:920) until it completes. A lge_tick dequeued while a stop-path thread holds the serializer parks on it and deadlocks the stopper (AB-BA): admin thread D-state forever, interface serializer never released, and the parked CPUs softclock thread stops delivering every callout on that CPU. lge_intr :1055 additionally calls callout_stop() from interrupt-thread context (PHY_INTR), wedging the driver ithread under the same conditions. Guest replica: with the exact lge_tick/lge_stop shapes, callout_stop() NEVER returned (caller stuck 1223 D4 costp sysctl kern.lgereplica.stop_lge=1, dmesg ends at IF THIS IS THE LAST lgereplica LINE, THE LGE_STOP AB-BA DEADLOCK IS PROVEN); negative control with the recommended-fix shape (lwkt_serialize_try + 1-tick retry) stopped cleanly under a held serializer (pending=0), live-validating the fix mechanism. On lge hardware the trigger is any lge_stop racing the 1 Hz stat tick: ifconfig lge0 down/up, MTU change, kldunload/detach, watchdog reinit, RXCMDFIFO-empty reinit, PHY interrupt.",
  "exploit_chain": "",
  "evidence": "findings/poc/DF-4316/VERDICT.md; run.log (control: RESULT pending=0 ... does NOT deadlock; positive: handler PARKED ... entering callout_stop() then silence, ps 1223 D4 costp sysctl kern.lgereplica.stop_lge=1 captured from a second session); build.log (module built on guest, BUILD_RC=0, -Werror clean); env.txt (guest uname, no lge NIC)",
  "kernel_refs": [
    "sys/dev/netif/lge/if_lge.c:435",
    "sys/dev/netif/lge/if_lge.c:984",
    "sys/dev/netif/lge/if_lge.c:1017",
    "sys/dev/netif/lge/if_lge.c:1055",
    "sys/dev/netif/lge/if_lge.c:1211",
    "sys/dev/netif/lge/if_lge.c:1319",
    "sys/dev/netif/lge/if_lge.c:1442",
    "sys/dev/netif/lge/if_lge.c:576",
    "sys/dev/netif/lge/if_lge.c:1029",
    "sys/dev/netif/lge/if_lge.c:1391",
    "sys/kern/kern_timeout.c:920",
    "sys/kern/kern_timeout.c:1091"
  ],
  "poc_changes": "No seed PoC. Authored lge_callout_replica.ko (out-of-tree kmodule replicating lges exact one-callout protocol: blocking serialize_enter, self re-arm inside the lock at hz period; stopper sysctl holding the serializer) because the guest has no LGE NIC; the deadlock lives in the generic protocol lge uses, so the replica exercises the identical kernel paths.",
  "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": 600,
  "guest_dirty": 0,
  "build_cmd": "vm.sh run_root cd /root/lgerep && make",
  "run_cmd": "vm.sh run_root kldload /root/lgerep/lge_callout_replica.ko; sysctl kern.lgereplica.arm=1; sleep 2; sysctl kern.lgereplica.stop_fix=1; (sysctl kern.lgereplica.stop_lge=1 &); second session: ps -axo pid,stat,wchan,command | grep stop_lge; dmesg | tail",
  "code_hash": "4c7827bbf2df15ca07406f0300971fdd0707e5e1743bf76234615251d051e871",
  "notes": "Family member of DF-3715 (em, Medium) and DF-0325 (netgraph pcb_lock): sync callout_stop under a lock the callback needs. Deterministic on the guest via replica; probabilistic on hardware (1 Hz tick vs stop-path window, repeatable triggers). Impact when it fires: permanent hang of the admin/ithread, leaked interface serializer, CPU-wide callout starvation (tsleep timeouts stop on that CPU). fix.diff = try-enter + 1-tick retry, mechanism validated live by the replica negative control.",
  "recommended_fix": "Make lge_tick() non-blocking: lwkt_serialize_try(ifp->if_serializer) with a 1-tick callout_reset retry on failure (fix.diff).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff authored against the read-only sys/ tree (applies cleanly to sys/dev/netif/lge/if_lge.c). Cannot be validated end-to-end on a guest without lge hardware; the replicas negative control demonstrates the fixed protocol shape does not deadlock under the identical stop-while-held-serializer scenario (callout_stop returned, pending=0).",
  "fix_evidence": "findings/poc/DF-4316/fix.diff; findings/poc/DF-4316/run.log (stop_fix control)"
}