DF-4235 / verdict.json
{ "finding_id": "DF-4235", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Source-certain Low: rl_rxeof() reacts to ANY inbound frame the 8139 flags bad by running a full rl_init() in interrupt context under the ifnet serializer (if_rl.c:1028-1032) -- chip quiesce + register reprogram + rl_setmulti TAILQ/crc32 walk + mii_mediachg MDIO bit-bang with DELAY(1) loops + callout_reset, flushing all queued TX. A remote peer sending corrupted (bad-CRC/runt) frames at 100Mbit line rate (~148k/s) keeps the adapter in a permanent re-init cycle: cheap remote DoS amplifier on real hardware. Runtime honestly inconclusive: the audit guest DOES attach a QEMU-emulated rtl8139 (rl0@pci0:0:4:0, chip 0x813910ec) and ~1250 frames were pushed through rl_rxeof live, but QEMUs model delivers every frame with ROK set (no CRC/runt error injection), so the error branch is unreachable on this guest. The live session doubles as negative evidence that rl_rxeofs length handling is memory-safe (m_devget chunking, uint16_t cur_rx == RL_RXBUFLEN wrap).", "exploit_chain": "remote attacker on the wire -> line-rate bad frames -> per-frame full re-init under serializer -> interface effectively down + one core pinned; no memory-safety impact (Low). RL_ISR_SYSTEM_ERR frames additionally buy a up-to-10ms rl_reset busy-wait (if_rl.c:690-703).", "evidence": "run.log (functional live evidence + RX_ERR unreachability), guest_env.txt (attach proof), rl_live_test.sh/rl_repro.sh, DF-4236/run.log (full churn logs), VERDICT.md (cost breakdown path:line), fix.diff", "kernel_refs": [ "sys/dev/netif/rl/if_rl.c:1028", "sys/dev/netif/rl/if_rl.c:1030", "sys/dev/netif/rl/if_rl.c:1270", "sys/dev/netif/rl/if_rl.c:1400", "sys/dev/netif/rl/if_rl.c:1458", "sys/dev/netif/rl/if_rl.c:1483", "sys/dev/netif/rl/if_rl.c:690", "sys/dev/netif/rl/if_rlreg.h:407" ], "poc_changes": "No seed (first finding for this file). Live traffic harness authored (rl_live_test.sh) using slirps virtual DNS (10.0.2.3) as an rl0-scoped peer so the ssh route via vtnet0 is never touched.", "attempts": 3, "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": 420, "guest_dirty": 0, "build_cmd": "n/a (shell-only PoC)", "run_cmd": "sh /tmp/rl_live_test.sh (guest, rtl8139 launcher)", "code_hash": "06943493e459723cc13e689177a949de1f366da8364a2a732976dec0815e372b", "notes": "Negative results established live + source: rl_rxeof length trust is safe (m_devget chunks; cur_rx uint16_t wraps at RL_RXBUFLEN=65536 exactly; rx_bytes window + wrap-branch m_copyback stay in-bounds even for a lying length); TX 4-slot ring always mod-4; MTU capped by ether_ioctl; eeprom/MII/setmulti walks bounded; attach-fail -> rl_detach cleanup symmetric.", "recommended_fix": "On ordinary per-frame RX errors, account and skip the descriptor instead of rl_init(); keep reset-class handling on RL_ISR_SYSTEM_ERR (see fix.diff).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff advances CAPR past the bad descriptor using the status words length field (valid for error frames per datasheet) and continues the loop. Not runtime-validable: the error branch cannot be staged on QEMUs model.", "fix_evidence": "fix.diff; run.log (ROK always set on guest deliveries)" } |