DF-3849 / verdict.json
{ "finding_id": "DF-3849", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Source-level certain: dc_rxeof (sys/dev/netif/dc/if_dc.c:2500-2607) takes the received frame length solely from the NIC-DMAd descriptor status word - total_len = DC_RXBYTES(rxstat) at :2523, a 14-bit field (DC_RXSTAT_RXLEN 0x3FFF0000, if_dcreg.h:415) - subtracts ETHER_CRC_LEN at :2568 and assigns it to the freshly re-posted mbuf at :2583 (quick path, dc_quick=1 default) or copies it out via m_devget at :2590, with NO bound against the posted cluster (2040 usable bytes: MCLBYTES=2048 minus the m_adj(8) in dc_newbuf, if_dc.c:2331). The only gate (:2548-2565) reads error bits from the same device-produced word and explicitly tolerates GIANT. A device-written descriptor with RXERR clear and FRAMESZ=16383 yields m_len=16379 on a 2040-byte cluster: up to 14339 bytes of heap over-read handed to the stack (BPF taps -> userspace info leak; header walks within the claimed length). FRAMESZ<4 wraps total_len negative (m_len ~4GB unsigned) -> stack OOB walk/panic. Precondition: malicious or misbehaving dc(4) device (buggy clone silicon among the ~15 families this driver binds, attacker-controlled PCIe/Thunderbolt DMA, or platform DMA corruption) - same trust boundary as DF-3604 (bce). Guest runtime: pci/if_dc present in kernel and /boot/kernel/if_dc.ko exists, but the audit guest has only virtio hardware (ifconfig: vtnet0 lo0; pciconf: QEMU i440fx + virtio only), so the descriptor-DMA precondition cannot be produced -> inconclusive per family convention. The exact integer mechanics were executed on the guest via trigger.c (build.log/run.log): FRAMESZ=16383 -> m_len=16379 (OOB READ 14339 bytes); FRAMESZ=2 -> m_len=-2 (unsigned ~4294967294).", "exploit_chain": "", "evidence": "findings/poc/DF-3849/VERDICT.md; run.log (hardware-absence probe: ifconfig/pciconf/kldstat + trigger output); build.log (trigger built and executed on guest); fix.diff (clamp: drop frames with total_len<=0 || total_len > MCLBYTES-8, re-post cluster)", "kernel_refs": [ "sys/dev/netif/dc/if_dc.c:2523", "sys/dev/netif/dc/if_dc.c:2548", "sys/dev/netif/dc/if_dc.c:2568", "sys/dev/netif/dc/if_dc.c:2583", "sys/dev/netif/dc/if_dc.c:2590", "sys/dev/netif/dc/if_dc.c:2321", "sys/dev/netif/dc/if_dc.c:2343", "sys/dev/netif/dc/if_dcreg.h:415", "sys/dev/netif/dc/if_dcreg.h:465" ], "poc_changes": "No seed PoC. Authored trigger.c (userspace arithmetic replica using the macros verbatim from if_dcreg.h) + hardware-absence probe, mirroring the DF-3604 (bce) family convention.", "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": 120, "guest_dirty": 0, "build_cmd": "cc -O2 -o /root/trigger /root/trigger.c", "run_cmd": "vm.sh run_root /root/trigger; ifconfig -l; pciconf -lv | head; kldstat -v | grep -m2 if_dc", "code_hash": "d29d43eba0dca07b26335c617d23658f63bce9c85afbab5bf02ef9de3138ec15", "notes": "dc(4) instance of the DF-3604 (bce) NIC-length-trust family. Distinct from known DF-1248/DF-1249 (SROM parse OOB; pnic_rx_bug_war unbounded copy): this is the mainline dc_rxeof path, hit by every chip the driver supports, not just PNIC SROM/FIFO-bug corner cases.", "recommended_fix": "Clamp total_len after the CRC subtraction: drop (ierrors++, re-post cluster) frames with total_len <= 0 or total_len > MCLBYTES - sizeof(u_int64_t).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored against the read-only sys/ tree (git apply --check clean). Cannot be driver-level validated on this guest (no dc hardware); the clamp is a static bounds check identical in shape to the error-path handling already in dc_rxeof.", "fix_evidence": "findings/poc/DF-3849/fix.diff" } |