⬢ DragonFlyBSD Kernel Audit
DF-3353 / verdict.json
← back to finding ↓ download raw
{
 "finding_id": "DF-3353",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "panic",
 "confidence": "certain",
 "verdict": "Deleting a pipe calls dn_unref_pipe_cb() (ip_dummynet3.c:1618-1629) which purges each child flowsets queues via purge_flow_set(fs,0) but never resets fs->backlogged. After the pipe is recreated (same number) and the flowset sees traffic again, backlogged counts stale+live flows. Deleting the flowset then executes p->sum -= fs->weight * fs->backlogged (:1681) with the inflated counter, driving the NEW pipes sum negative; the next new flow on another flowset of that pipe brings sum to exactly 0, and its packet move hits p->V += (len << MY_M) / p->sum (:574) -> integer divide fault. Reproduced 1/1 on the stock guest; the serial console shows the smoking gun ++ ref to pipe 1 from fs 300 (the dn_unref_pipe_cb kprintf at :1624) immediately before Fatal trap 18 ... Stopped at ready_event_wfq+0xd1: divq %rcx,%eax. Identical code and mechanism to v1 DF-3351 (sys/net/dummynet/ip_dummynet.c), which pass-1 of this file missed.",
 "exploit_chain": "root: slow pipe 1 + fs300(mask dst-port,w=1) + fs400(w=1); 2 pkts to flow A (fs300) so one stays backlogged; DEL pipe 1 (queues purged, backlogged stays 1, sum dies with pipe); recreate pipe 1; 2 pkts to flow B on fs300 (backlogged=1stale+1live, sum=1); DEL fs300 -> sum = 1-1*2 = -1; 1 pkt to flow C on fs400 -> sum=0; the pending wfq timer fire (~1.4s) moves Cs packet -> divq by zero -> panic. No uid=0.",
 "evidence": [
  "panic.txt: ++ ref to pipe 1 from fs 300 then Fatal trap 18, ready_event_wfq+0xd1 divq",
  "run.log: six-step drift dance transcript + console panic",
  "dfv3.c: drift subcommand"
 ],
 "kernel_refs": [
  "sys/net/dummynet3/ip_dummynet3.c:1618",
  "sys/net/dummynet3/ip_dummynet3.c:1627",
  "sys/net/dummynet3/ip_dummynet3.c:1681",
  "sys/net/dummynet3/ip_dummynet3.c:574"
 ],
 "poc_changes": "PoC authored fresh (multi-trigger dfv3.c): raw setsockopt with ABI structs verbatim from ip_dummynet3.h; ipfw3 (not ipfw) `queue <fs> udp ... via lo0` rules divert lo0 UDP into the WF2Q path; setup.sh sets net.filters_default_to_accept=1 before kldload ipfw3 because ipfw3 defaults to deny-all and otherwise locks out ssh.",
 "attempts": 1,
 "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": 60,
 "guest_dirty": 0,
 "build_cmd": "in-guest root: cc -O -o /tmp/dfv3 /tmp/dfv3.c",
 "run_cmd": "in-guest root: sh /tmp/setup.sh && /tmp/dfv3 drift",
 "code_hash": "9f8b5ab161a2890df06ccaf75ed7ddbbb746efb2f2320c50d209317fe5b42c09",
 "notes": "dummynet3.ko + ipfw3.ko + ipfw3_basic.ko kldloaded; triggers are UDP to 127.0.0.1 matched by ipfw3 queue rules. Panic findings verified once each on the stock INVARIANTS kernel; DF-3356 verified with control run. v1 twin (sys/net/dummynet/ip_dummynet.c) carries the same code.",
 "recommended_fix": "reset fs->backlogged=0 in dn_unref_pipe_cb before purging (fix.diff hunk 6)",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Fri Sep 11 07:37:10 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (dummynet3 fix.diff applied, rebuilt via make nativekernel)",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "Union fix.diff (all five DF-3352..DF-3356 hunks; this findings hunk is the one noted in recommended_fix) applied to the guest /usr/src, kernel rebuilt with make -j6 nativekernel KERNCONF=X86_64_GENERIC and installed (>>> Kernel build completed 07:41:12, install completed 07:42:30 UTC). fs->backlogged=0 reset in dn_unref_pipe_cb: the full 6-step drift dance completes with no divide fault (baseline panicked ~1.4s after the last packet). All other four PoCs re-run on the same patched kernel: all clean.",
 "fix_evidence": [
  "run.2.log (patched-kernel validation transcript)",
  "fix.diff (git-apply-able unified diff, 6 hunks vs sys/net/dummynet3/ip_dummynet3.c)"
 ]
}