tulip_rx_intr() RX-replenish path panics on bus_dmamap_load_mbuf() failure instead of backing off (local DoS)
Summary
When replenishing a receive descriptor fails to DMA-map the freshly allocated cluster (BUS_DMA_NOWAIT; RX data tag capped at BUS_SPACE_MAXADDR_32BIT, so clusters above 4 GB need bounce pages), the driver executes panic("tulip_rx_intr") - flagged /* XXX */ by its author - instead of dropping the buffer and backing off like the m_getcl() failure path 12 lines up. VERIFIED: the panic string is live in /boot/kernel/kernel (strings hit on the guest); zero de instances exist so not executable there. Machine with a de(4) NIC, >4 GB RAM and DMA-map pressure: any local user driving receive traffic can probabilistically trigger a kernel panic - unprivileged local DoS. Fix: mirror the existing graceful degrade.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3758 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| panic_path_evidence.sh | β | 1.4 KB | view raw | |
| build.sh | β | 90 B | view raw | |
| run.sh | β | 106 B | view raw | |
| build.log | β | 72 B | view raw | |
| run.log | β | 1.3 KB | view raw | |
| env.txt | β | 865 B | view raw | |
| fix.diff | β | 716 B | view raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| manifest.json | β | 886 B | view raw | |
| verdict.json | β | 2.5 KB | view raw |
DF-3758 VERDICT β panic("tulip_rx_intr") on RX DMA-map failure
Status: not_reproduced (no de(4) hardware on the guest; panic path is
compiled into the running kernel and source-certain)
Root cause
/* sys/dev/netif/de/if_de.c:3127-3150 (RX replenish loop) */
do {
...
ms->m_len = ms->m_pkthdr.len = TULIP_RX_BUFLEN;
error = bus_dmamap_load_mbuf(ri->ri_data_tag, *nextout->di_map, ms,
tulip_dma_map_rxbuf, nextout->di_desc, BUS_DMA_NOWAIT);
if (error) {
device_printf(sc->tulip_dev,
"unable to load rx map, error = %d\n", error);
panic("tulip_rx_intr"); /* XXX */ <-- if_de.c:3139
}
...
} while ((ms = me) != NULL);
The RX data tag is created with BUS_SPACE_MAXADDR_32BIT
(if_de.c:3945-3947), so on machines with memory above 4 GB every rx
cluster that lands high must bounce; bus_dmamap_load_mbuf(..., BUS_DMA_NOWAIT)
can then fail transiently (bounce-page pool exhausted / allocation failure
under memory pressure). The driver answers with an unconditional panic()
β the author even flagged it /* XXX */.
Compare the correct pattern 12 lines up: when m_getcl() fails
(if_de.c:3114-3122) the driver drops to fillok = 0; TULIP_RXBUFSLOW;
continue; and retries from the watchdog. The map-load failure deserves the
same treatment: free the mbuf chain, back off, retry later.
Reachability
- Requires a machine with a de(4) NIC receiving traffic (any local user can generate RX by being sent packets / requesting traffic) plus enough memory/DMA-map pressure to make a NOWAIT map-load of a >4 GB cluster fail. Classic environmental-trigger local DoS; probability grows with RAM size, rx interrupt rate, and vm pressure.
- Audit guest:
deis compiled into X86_64_GENERIC and the panic string is present in/boot/kernel/kernel(run.log:stringshits), but there are zero DEC 2114x PCI devices and zerohw.de.*OIDs β the path cannot execute. Hencenot_reproduced/ impact none on this rig.
Evidence collected on the guest (run.log)
strings /boot/kernel/kernel | grep tulip_rx_intrβ 3 hits β the panic is live in the running kernel binary./usr/src/sys/dev/netif/de/if_de.c:3127-3145excerpt β exact code.kldstat -v | grep pci/if_deβ 1 (driver compiled in);sysctl -a | grep -c ^hw\.de\.β 0 (never attached).
Recommended fix
Drop the buffer and back off instead of panicking (fix.diff): free the
remaining chain, clear fillok, set TULIP_RXBUFSLOW, and break out of the
replenish loop β the watchdog (if_de.c:3806-3807) retries later, exactly
like the existing m_getcl failure path.
Kernel references
- sys/dev/netif/de/if_de.c:3134-3140 (panic), 3106-3122 (correct degrade pattern for alloc failure), 3945-3947 (32-bit-limited RX data tag), 3806-3807 (watchdog retry)
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testablefix.diff authored (drop + back off instead of panic); not kernel-tested: no de(4) hardware on the guest.
fix.diff mirrors the existing graceful path at if_de.c:3114-3122.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log: 'strings /boot/kernel/kernel' shows 3 tulip_rx_intr hits; if_de.c:3127-3145 excerpt with panic("tulip_rx_intr") at 3139; 'kldstat pci/if_de: 1' and 'hw.de.* sysctl OIDs: 0'.
PoC changes
No seed PoC; authored the shell evidence collector from scratch.
Verified recommended fix
Replace panic() at if_de.c:3139 with m_freem(ms); fillok=0; TULIP_RXBUFSLOW; break β mirroring the m_getcl failure path.
Verdict
tulip_rx_intr()'s replenish loop panics unconditionally when bus_dmamap_load_mbuf(BUS_DMA_NOWAIT) fails (if_de.c:3139, marked / XXX / by the author). The RX data tag is capped at BUS_SPACE_MAXADDR_32BIT (if_de.c:3945-3947), so on hosts with memory above 4 GB high clusters need bounce pages and the NOWAIT load can fail under DMA-map/memory pressure while a local user drives receive traffic -> kernel panic (local DoS). The correct degrade pattern exists 12 lines earlier for m_getcl failure (if_de.c:3114-3122: fillok=0, TULIP_RXBUFSLOW, retry via watchdog if_de.c:3806); the map-load failure should do the same. Guest evidence (run.log): panic string live in /boot/kernel/kernel, source confirmed in /usr/src, driver compiled into X86_64_GENERIC but zero attached instances (hw.de.* count=0, no 0x1011 PCI devices) -> path cannot execute on this rig, hence not_reproduced; expected impact on de-equipped hosts is a reliable-if-probabilistic unprivileged local panic.
No comments yet.