DF-4045 / verdict.json
{ "finding_id": "DF-4045", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Source-level certain, runtime hardware-gated (inconclusive per family convention). stge_rxeof takes the received frame length from the NIC-DMAd 16-bit RFD DMAFrameLen field (RFD_RxDMAFrameLen(x) = x & 0xffff, if_stgereg.h:167) and applies it with only a per-interface bound: on the FrameEnd descriptor if_stge.c:1606-1607 sets mp->m_len = RFD_RxDMAFrameLen(status) - sc->sc_cdata.stge_rxlen and the ONLY check (:1623-1625) is m->m_pkthdr.len > sc->sc_if_framesize afterwards. (1) With jumbo MTU (SIOCSIFMTU accepts up to STGE_JUMBO_MTU=9004, :1261; framesize=9022, :2013) a single FrameEnd claiming framelen=9022 yields m_len=9022 on the 2048-byte MCLBYTES cluster from stge_newbuf (m_getcl, :2326-2329) -> 6974-byte heap OOB read into a delivered frame (BPF tap, m_copydata pulls, header reads past 2KB) -> kernel-heap info leak to any local user with a socket/BPF on stgeX (k=1/2/3 intermediates: 4926/2878/830 bytes). (2) At ANY MTU including default, framelen < rxlen gives a NEGATIVE tail m_len that passes the framesize check (pkthdr.len = framelen) and survives ether_inputs m_len < ETHER_HDR_LEN first-mbuf guard; m_copydata (uipc_mbuf.c:1689) computes unsigned count = min(m->m_len - off, len) on it -> ~4GB bcopy -> panic/UB when any consumer walks the chain. Legit frames never violate either bound (intermediates contribute full 2046/2048-byte buffers, remainder < 2048), so any violation is a device lie. Runtime: guest has virtio-net only, zero Sundance/Tamarack PCI devices (env.txt); trigger.c (geometry model) builds and runs on the guest RC=0 (run.log). stge(4) instance of the DF-3604(bce)/3759(alc)/3933(age)/3989(ale)/4024(re) family; pass 1 filed nothing against this file.", "exploit_chain": "On real (faulty/malicious) IP1000A/TC9021 hardware: DMA an RFD status quadword with RFDDone|FrameEnd and a lying 16-bit DMAFrameLen -> case A (jumbo MTU) m_len up to 9022 over a 2048-byte cluster -> leaked kernel-heap bytes ride into received packet payloads readable by any local user with a socket on the interface; case B (default MTU) negative tail m_len -> m_copydata unsigned count -> ~4GB bcopy -> panic (BPF attached). Inbound-only: read/DoS primitive, no write.", "evidence": "run.log (model output: 6974/4926/2878/830-byte OOB extents, negative m_len -1946 -> ~4GB bcopy, RC=0); build.log; env.txt (vtnet0+lo0 only, NO_STGE_HW); VERDICT.md (path:line trace); trigger.c; fix.diff (FrameEnd m_len bounded to [0, MCLBYTES] with drivers own drop idiom)", "kernel_refs": [ "sys/dev/netif/stge/if_stge.c:1606", "sys/dev/netif/stge/if_stge.c:1607", "sys/dev/netif/stge/if_stge.c:1608", "sys/dev/netif/stge/if_stge.c:1623", "sys/dev/netif/stge/if_stge.c:1625", "sys/dev/netif/stge/if_stge.c:1261", "sys/dev/netif/stge/if_stge.c:2013", "sys/dev/netif/stge/if_stge.c:2326", "sys/dev/netif/stge/if_stge.c:2329", "sys/dev/netif/stge/if_stge.c:2335", "sys/dev/netif/stge/if_stgereg.h:167", "sys/dev/netif/stge/if_stgevar.h:55", "sys/kern/uipc_mbuf.c:1689" ], "poc_changes": "No seed PoC (pass 1 filed nothing). Authored trigger.c as hardware-precondition + cluster-geometry model per the DF-3989/DF-4024 family convention; honest hardware-absent verification.", "attempts": 2, "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": "cc -O2 -Wall -Wextra -o /root/tr4045 /root/tr4045.c", "run_cmd": "vm.sh run_root /root/tr4045; echo RC_4045=$?", "code_hash": "915f6da37c8aceec22444c22bee6db6494a642ac16b404f27867a73ec55c9406", "notes": "Pass-2 checklist item (a) for if_stge.c. Checked and cleared this pass on the same surface: txeof producer/consumer ring arithmetic (tx_cnt==busyq invariant, HIWAT 248 < RING_CNT 256 so tx_prod can never lap tx_cons, init TFDDone markers + empty busyq -> early return, control read guarded by tx_cnt check); stge_encap freeq non-empty guaranteed by HIWAT gate, error path frees mbuf before any queue mutation; dma_alloc failure paths pair with dma_free; SIOCSIFMTU bounded ETHERMIN..9004; no WOL surface. Residual not filed: stge_tx_error register-drain loop bounded only by hardware clearing TS_TxComplete (device-gated DoS, weaker than the DF-3761 unbounded-walk shape).", "recommended_fix": "In stge_rxeof drop the frame when the FrameEnd mp->m_len is < 0 or > MCLBYTES (see fix.diff); legit frames never trip either bound.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applies clean (git apply --check) and uses the drivers own chain-free + STGE_RXCHAIN_RESET + continue idiom on the already-compiled path; runtime validation would need stge(4) hardware.", "fix_evidence": "findings/poc/DF-4045/fix.diff" } |