DF-3808 / verdict.json
{ "finding_id": "DF-3808", "status": "untested", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Source-certain structural finding, not PoC-verified (untested): the trigger input is written by the NIC (status-LE TX-index fields / STAT_TXAx_RIDX register), not by packet contents, so it cannot be exercised on the audit guest (no Marvell hardware; NO_MARVELL_PCI, vtnet0 lo0 only; src md5 == audited tree). msk_txeof (sys/dev/netif/msk/if_msk.c:2884-2927) consumes the device-supplied done index with NO range validation: mskc_handle_events feeds it status & STLE_TXA1_MSKL and the port-B composite (if_msk.c:3206-3217) - 11-bit values, 0..4095 - and msk_watchdog feeds a raw 16-bit CSR read (if_msk.c:2671-2674), while the ring holds MSK_TX_RING_CNT = 256 entries (if_mskreg.h:2309). For an idx that can never equal cons (>= 256, or behind cons), the walk terminates only via the msk_tx_cnt <= 0 break (:2901-2902) and therefore force-completes the ENTIRE in-flight window: bus_dmamap_unload + m_freem on mbufs whose TX LEs still carry HW_OWNER (:2910-2916) - the NIC may still be DMA-reading those clusters (stale/freed kernel memory transmitted on the wire, dmamap desync) - and leaves msk_tx_cons advanced to msk_tx_prod while the NIC prefetch unit still walks the old LEs (subsequent msk_encap overwrites hardware-owned descriptors). Memory-safe by construction: the walk is bounded by msk_tx_cnt (<= 256) and MSK_INCs modulo keeps all array access in-bounds; every visited EOP descriptor has a non-NULL tx_m (set at :2575-2579) and m_freem(NULL) is guarded (sys/kern/uipc_mbuf.c) - hence Low severity, device-trust/defense-in-depth class (same trust boundary as DF-3604/DF-3807). fix.diff adds a one-line range check at the top of msk_txeof(), covering both call sites.", "exploit_chain": "", "evidence": "VERDICT.md (full path:line trace incl. the 0..4095 vs 256 arithmetic and the bounded-drain proof); fix.diff (range check, git apply --check clean); env.txt (no Marvell hardware on guest; untested rationale)", "kernel_refs": [ "sys/dev/netif/msk/if_msk.c:2884", "sys/dev/netif/msk/if_msk.c:2900", "sys/dev/netif/msk/if_msk.c:2901", "sys/dev/netif/msk/if_msk.c:2910", "sys/dev/netif/msk/if_msk.c:2671", "sys/dev/netif/msk/if_msk.c:3206", "sys/dev/netif/msk/if_mskreg.h:2198", "sys/dev/netif/msk/if_mskreg.h:2309" ], "poc_changes": "No PoC authored: hardware-gated device-trust finding (Low severity). Analysis-only pack.", "attempts": 0, "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": 0, "guest_dirty": 0, "build_cmd": "", "run_cmd": "", "code_hash": "32d9b09b23a3ee2822b6f413b4a9d7286f272cd4331e94436666d30826d9c1a9", "notes": "Pass-2 finding (pass 1 marked the file clean). Skipped live verification per contract (Low severity, device-trust class, no msk hardware on the audit guest); structural claim is certain from source, impact is speculative (requires faulty/hostile NIC or errant DMA).", "recommended_fix": "Range-check the device-supplied done index at the top of msk_txeof(): reject idx < 0 || idx >= MSK_TX_RING_CNT (see fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff adds the one-line range check at the top of msk_txeof(), covering both call sites (status LE and watchdog register). Not validated at runtime: requires Yukon hardware to exercise msk_txeof; the change is trivially local and cannot affect the valid-index path.", "fix_evidence": "fix.diff" } |