⬢ DragonFlyBSD Kernel Audit
DF-4025 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4025",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Mechanism PROVEN on the guest kernel by a deterministic replica of if_re.cs exact callout/serializer protocol (re(4) instance of DF-3715); re-specific runtime hardware-gated, inconclusive per family convention. re_attach arms one MP callout (callout_init_mp(&sc->re_timer), if_re.c:604; scheduled every hz by re_init :1881) whose callback re_tick BLOCKS on the driver serializer by hand (lwkt_serialize_enter, if_re.c:1358) and self re-arms UNDER it (re_tick_serialized :1371-1380). re_stop(sc,TRUE) issues callout_stop(&sc->re_timer) at if_re.c:2000 while HOLDING that serializer (every caller holds it: re_detach :909-911, re_suspend :2045-2047, re_shutdown :2116-2118, re_ioctl SIOCSIFFLAGS :1918, re_init :1871), clearing IFF_RUNNING only afterwards (:2001). DragonFly callout_stop is synchronous (kern_timeout.c:1091 -> _callout_cancel_or_stop sync=1; ssleep \"costp\" at :923), so a re_tick dequeued on another CPU and parked in lwkt_serialize_enter deadlocks against the stopper (AB-BA): both threads wedge permanently, if_serializer is never released (all later ioctls/if_start on reX block), and the affected CPUs softclock stops delivering ALL callouts. Guest replica (rereplica.ko, build.log BUILD_RC=0): arm -> re_tick-shape handler parks (fired=720 ran=719) -> re_stop-shape stop under the serializer -> dmesg ends at the AB-BA DEADLOCK IS PROVEN marker and NEVER prints the post-callout_stop line; stopper thread 1120 stuck D5/wchan costp across samples; the wedged guest could subsequently NOT be shut down cleanly (callout starvation) and was force-reset with-src. Triggers on re hardware: ifconfig reX down/up, SIOCSIFCAP reinit, detach, suspend, reboot -- racing the 1 Hz tick.",
  "exploit_chain": "On re(4) hardware: any re_stop(TRUE) (root-triggered ifconfig down/detach/suspend/reboot) racing the 1 Hz re_tick on another CPU -> permanent kernel deadlock of two threads + CPU-wide callout starvation; system degradation DoS, not memory corruption.",
  "evidence": "run.log (replica session transcript: dmesg marker sequence ending at AB-BA DEADLOCK IS PROVEN, ps 1120 D5 costp sysctl kern.rereplica.stop=1 across two samples, stop output never completing, plus the failed clean shutdown); build.log (module built on guest, BUILD_RC=0); rereplica.c/Makefile; env.txt (no re hardware, md5 match); fix.diff (non-blocking re_tick)",
  "kernel_refs": [
    "sys/dev/netif/re/if_re.c:604",
    "sys/dev/netif/re/if_re.c:1354",
    "sys/dev/netif/re/if_re.c:1358",
    "sys/dev/netif/re/if_re.c:1371",
    "sys/dev/netif/re/if_re.c:1380",
    "sys/dev/netif/re/if_re.c:1871",
    "sys/dev/netif/re/if_re.c:1881",
    "sys/dev/netif/re/if_re.c:1918",
    "sys/dev/netif/re/if_re.c:1988",
    "sys/dev/netif/re/if_re.c:2000",
    "sys/dev/netif/re/if_re.c:2001",
    "sys/kern/kern_timeout.c:1091",
    "sys/kern/kern_timeout.c:923"
  ],
  "poc_changes": "No seed PoC. Authored rereplica.ko (out-of-tree kmodule) replicating res protocol: one MP callout whose handler does a BLOCKING lwkt_serialize_enter + under-lock conditional self re-arm (re_tick/re_tick_serialized shape), torn down by callout_stop under a held serializer in re_stops exact call order (stop then clear running). 1-tick arm period instead of hz to make the race window deterministic; real driver arms at hz.",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 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": 300,
  "guest_dirty": 0,
  "build_cmd": "cd /root/rereplica && make (bsd.kmod.mk)",
  "run_cmd": "kldload /root/rereplica/rereplica.ko; sysctl kern.rereplica.arm=1; sleep 2; (sysctl kern.rereplica.stop=1 >/tmp/stop4025.out 2>&1 &); sleep 6; dmesg | grep rereplica | tail -4; ps -o pid,stat,wchan,command -ax | grep stop=1",
  "code_hash": "16620f8023070bcb89f5e39235277f83ae6fbde1336994e3975aa43c8c65a4fb",
  "notes": "Pass-2 checklist item (c) for if_re.c (DF-3715 family re-arm question). The wedged guest surviving vm.sh down cleanly is itself evidence: callout starvation prevented an orderly poweroff; force-killed and reverted to the with-src snapshot (guest_dirty=0 post-reset). re_tick_serializeds early-return gate (:1371) cannot save the parked callback because re_stop clears IFF_RUNNING only AFTER callout_stop returns -- which it never does.",
  "recommended_fix": "Make re_tick() non-blocking: lwkt_serialize_try() with a 1-tick callout_reset retry when the serializer is busy (see fix.diff; the same shape proposed for em_timer in DF-3715).",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (try-enter + 1-tick retry in re_tick) applies clean against the read-only tree; a runtime fix-validation would need re(4) hardware to race re_stop against a real re_tick. The replicas wedged protocol is exactly what the fix removes.",
  "fix_evidence": "findings/poc/DF-4025/fix.diff"
}