β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3758

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3758 Β· 10 files
FileTypeDescriptionSize
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
VERDICT.md
↓ download 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: de is compiled into X86_64_GENERIC and the panic string is present in /boot/kernel/kernel (run.log: strings hits), but there are zero DEC 2114x PCI devices and zero hw.de.* OIDs β€” the path cannot execute. Hence not_reproduced / impact none on this rig.

Evidence collected on the guest (run.log)

  1. strings /boot/kernel/kernel | grep tulip_rx_intr β†’ 3 hits β€” the panic is live in the running kernel binary.
  2. /usr/src/sys/dev/netif/de/if_de.c:3127-3145 excerpt β€” exact code.
  3. kldstat -v | grep pci/if_de β†’ 1 (driver compiled in); sysctl -a | grep -c ^hw\.de\. β†’ 0 (never attached).

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_testable
baseline no→ patch + rebuild →patched clean

fix.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.
↓ fix.diffper-fix-DF-3758

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.