dc_pnic_rx_bug_war unbounded copy loop - heap overflow if PNIC produces >5 descriptors
Summary
dc_pnic_rx_bug_war at if_dc.c:2418-2429: while(1) { bcopy(mtod(m,char*),ptr,DC_RXLEN); ptr+=DC_RXLEN; } with NO iteration cap. Buffer is kmalloc(DC_RXLEN*5=7680). If PNIC 82c168/169 chip produces >5 descriptors for a corrupted RX event, ptr overflows into adjacent kernel heap. Also total_len=DC_RXBYTES(rxstat) at :2432 (14-bit 0-16383) used in bcopy(ptr,mtod(m,char*),total_len) at :2453 without checking <mbuf area (~2040B) -> cluster overflow. Trigger: flood PNIC NIC to hit known FIFO bug during heavy activity. Speculative: depends on chip producing >5 extra descs. Fix: cap loop at 5 iterations, clamp total_len.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1249 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | source trace, mechanism, impact ceiling, fix rationale | 3.3 KB | β raw |
| fix.diff | suggested-fix | cap dc_pnic_rx_bug_war loop at 5 iterations (DC_RXLEN*5 buffer) | 962 B | view raw |
| fix_build.log | build-log | if_dc.ko build with fix applied (52696 bytes, RC=0) | 6.7 KB | view raw |
| build.sh | build-log | repro: apply-check + note | 378 B | view raw |
| run.sh | run-log | no runtime trigger (no PNIC NIC) | 404 B | view raw |
| env.txt | environment | uname, cc version, module state | 359 B | view raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1249 β dc_pnic_rx_bug_war unbounded copy loop (heap overflow if PNIC produces >5 fragments)
Verdict
SOURCE-CONFIRMED (real code-level hardening gap, speculative exploitation), INCONCLUSIVE at runtime β the if_dc driver IS compiled into the running GENERIC kernel, but the guest's NIC is vtnet0 (virtio-net); no DEC 21143 / PNIC 82c168/82c169 NIC is attached, so dc_pnic_rx_bug_war is never called. Fix authored and compile-validated.
Mechanism (source trace)
dc_pnic_rx_bug_war() salvages a corrupted multi-fragment RX frame from the PNIC chip bug by copying fragments into a fixed salvage buffer:
sys/dev/netif/dc/if_dc.c:1976βsc->dc_pnic_rx_buf = kmalloc(DC_RXLEN * 5, M_DEVBUF, M_WAITOK);β buffer is 7680 bytes (5 Γ 1536).sys/dev/netif/dc/if_dc.c:2414-2429:ptr = sc->dc_pnic_rx_buf; /* 7680-byte buffer */ bzero(ptr, DC_RXLEN * 5); while (1) { c = &sc->dc_ldata->dc_rx_list[i]; rxstat = c->dc_status; m = sc->dc_cdata.dc_rx_chain[i]; bcopy(mtod(m, char *), ptr, DC_RXLEN); /* copies 1536 bytes each iteration */ ptr += DC_RXLEN; /* advances 1536, NO bound check */ if (i == idx || rxstat & DC_RXSTAT_LASTFRAG) break; dc_newbuf(sc, i, m); DC_INC(i, DC_RX_LIST_CNT); /* ring is 64 or 192 descriptors */ }- The loop's only termination is
i == idx(reaching the LASTFRAG descriptor index) orrxstat & LASTFRAG. There is no cap on the number of iterations. The caller (if_dc.c:2525-2533) enters this path whenFIRSTFRAGandLASTFRAGare not both set in the same descriptor, walking from the saved FIRSTFRAG index to the LASTFRAG index. - If the PNIC chip (or a corrupted descriptor ring) yields more than 5 fragments between FIRSTFRAG and LASTFRAG,
ptrwalks past the end of the 7680-bytedc_pnic_rx_bufinto adjacent kernel heap. The ring (DC_RX_LIST_CNT= 64 or 192,if_dcreg.h:459/461) bounds the worst case to 64β192 iterations β up to ~98KBβ294KB of overflow. - Additionally
total_len = DC_RXBYTES(rxstat)(if_dc.c:2432, 14-bit) is used unchecked inbcopy(ptr, mtod(m,char*), total_len)at line 2453.
Why not reproduced at runtime
- Guest NIC is
vtnet0(virtio-net). Thedcdriver probe never matches a virtio device, so nodc_softcwithDC_PNIC_RX_BUG_WARis ever instantiated. - The PNIC RX-bug war is gated by
sc->dc_flags & DC_PNIC_RX_BUG_WAR, set only forDC_TYPE_PNIC(if_dc.c:1973-1975), which requires a Lite-On 82c168/82c169 chip. - The finding's own confidence is speculative: it requires the PNIC chip to produce >5 descriptors for a single corrupted RX event (worse than the known bug the war was written to handle). This is a driver-robustness issue against misbehaving hardware, not an unprivileged-user-reachable bug.
Fix (fix.diff, compile-validated)
Cap the loop at 5 iterations (matching the DC_RXLEN * 5 buffer) so a misbehaving PNIC cannot overflow the salvage buffer. The patched if_dc.c builds cleanly into if_dc.ko (52696 bytes) with gcc 8.3, -Werror.
Realistic impact ceiling
Kernel heap overflow from a misbehaving PNIC NIC (worst-case ~294KB overflow). On this guest: not reachable (vtnet0 NIC, no PNIC). Defense-in-depth driver hardening.
Fix verification
not_testablecompile validated
module/object build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. dc_pnic_rx_bug_war unbounded loop vs 7680B buffer -> heap overflow. dc in GENERIC, no PNIC NIC.
No comments yet.