DF-3356 / verdict.json
{ "finding_id": "DF-3356", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "set_fs_parms() clamps qsize only against the upper bound and 0 (ip_dummynet3.c:1442-1448: `qsize > 1024*1024` / `qsize == 0 || qsize > 100`); a negative qsize passes both checks (slot mode) and is stored. dummynet_io() then enforces the limit with `if (q->len >= fs->qsize)` (:1104) where q->len is u_int and the negative int is converted to ~4e9, so the drop check never fires and the flow queue grows without bound, pinning kernel memory (mbuf clusters) until the system exhausts memory. Demonstrated live on the stock guest with no panic: hammering 400 packets into a qsize=-1 flowset yields len=399, drops=0 in the IP_DUMMYNET_GET dump, while the byte-for-byte identical control run with qsize=50 yields len=50, drops=349. Growth is bounded only by available kernel memory. Identical code to v1 DF-0376; missed by pass-1 of this file.", "exploit_chain": "root: setsockopt pipe{bw 8k} + fs{qsize=-1}; unprivileged UDP flood to a port diverted by an ipfw3 `queue` rule -> queue accumulates without limit -> kmallocd dn_pkt/mbuf exhaustion -> OOM DoS. No uid=0.", "evidence": [ "run.log: GET dump qsize=-1 (len=399 drops=0) vs control qsize=50 (len=50 drops=349)", "dfv3.c: qsize subcommand + GET walker" ], "kernel_refs": [ "sys/net/dummynet3/ip_dummynet3.c:1442", "sys/net/dummynet3/ip_dummynet3.c:1446", "sys/net/dummynet3/ip_dummynet3.c:1100", "sys/net/dummynet3/ip_dummynet3.c:1104" ], "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 qsize -1 400 (then control: qsize 50 400)", "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": "clamp negative qsize to the defaults in set_fs_parms (fix.diff hunk 4)", "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). set_fs_parms clamps qsize<=0 to 50 (slots): the qsize=-1 run now behaves byte-for-byte like the healthy control (len=50 drops=349); baseline held 399 packets with 0 drops. 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)" ] } |