bfe_intr error reinit performs M_WAITOK cluster allocations in interrupt context: BFE_ISTAT_ERRORS -> bfe_init -> bfe_newbuf(init=1) sleeps in the ithread holding the serializer
Summary
On any DMA/RX/TX error interrupt, bfe_intr calls bfe_init from interrupt context; the rx-ring re-init does 511 m_getcl(M_WAITOK) allocations inside the ithread holding the interface serializer. RDU fires exactly under cluster exhaustion, so the sleepable re-arm blocks precisely when the system cannot satisfy it - the IRQ line and every serializer-guarded entry point stall. The watchdog path is equally non-sleepable and hits the same M_WAITOK. NOWAIT-plumbing fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4305 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| env.txt | β | 1.2 KB | view raw | |
| fix.diff | β | 3.6 KB | view raw | |
| manifest.json | β | 905 B | view raw | |
| verdict.json | β | 3.1 KB | view raw |
DF-4305 β bfe_intr error reinit performs M_WAITOK cluster allocations in interrupt context
File: sys/dev/netif/bfe/if_bfe.c (pass 2; known DF-1509 not touched)
Lines: 1176-1187 (bfe_intr ISTAT_ERRORS β bfe_init), 1186, 1362-1386 (bfe_init), 1368 (bfe_list_rx_init), 538-554 (bfe_list_rx_init β bfe_newbuf(sc, i, 1)), 565 (m = m_getcl(init ? M_WAITOK : M_NOWAIT, ...)), 576 (if_printf in the load-fail path).
The bug
On any DMA/RX/TX error interrupt (BFE_ISTAT_ERRORS: DSCE/DATAE/DPE/RDU/
RFO/TFU), bfe_intr clears IFF_RUNNING and calls bfe_init from
interrupt context (if_bfe.c:1186). bfe_init β bfe_list_rx_init
(:1368) β bfe_newbuf(sc, i, 1) with init=1 β m_getcl(M_WAITOK)
(:565) for 511 clusters, plus bus_dmamap_load_mbuf_segment
(BUS_DMA_NOWAIT, safe) and β on load failure β if_printf (:576).
M_WAITOK may block indefinitely in the mbuf allocator when the cluster
zone is exhausted β in an interrupt thread holding the interface
serializer. The realistic trigger is self-reinforcing: RDU ("receive
descriptor underflow") fires precisely when the system is out of buffers
(the driver failed to refill the ring), i.e. exactly when the M_WAITOK
re-arm is most likely to sleep. A sleeping ithread stops servicing its IRQ
line; with the serializer held, every other entry point on the interface
blocks too; if the allocation depends on network progress to free
clusters, the wait can persist indefinitely (soft deadlock), otherwise it
stalls the interrupt pipeline for the allocation latency.
The TX-watchdog reinit path (bfe_watchdog :1470-1483 β bfe_init
:1480) runs in netisr context β equally non-sleepable β and hits the same
M_WAITOK.
Severity Low: requires the error interrupt (hardware/error conditions, typically under system-wide memory exhaustion); impact is a stalled interface/interrupt line rather than memory corruption.
Guest status
No BCM4401 hardware exists on this guest (env.txt: NO_BROADCOM_PCI,
hw.bfe unknown oid; QEMU has no device model), so the path cannot be
executed β not_reproduced per family convention (cf. DF-3827). The defect
is certain from source.
Build/run (nothing runnable without hardware)
See env.txt for the presence probes; fix.diff is the waitok plumbing.
Fix
Plumb a waitok flag: interrupt/watchdog reinit paths call
bfe_init_waitok(sc, 0) β bfe_newbuf uses M_NOWAIT (see fix.diff,
patch -p1 --dry-run clean).
DF-4305 VERDICT β sleepable allocation in bfe's interrupt-context reinit
Bottom line
Source-certain defect; runtime not reproducible on this guest (no BCM4401; QEMU cannot emulate one β env.txt). Recorded not_reproduced per family convention (DF-3827). Impact ceiling on real hardware: interrupt-thread stall / soft interface deadlock under cluster exhaustion β availability only, Low severity, no corruption, no leak.
Trace
bfe_intr(:1152-1200) processesistat & BFE_ISTAT_ERRORS(if_bfereg.h:40-41) βbfe_init(sc)at :1186 β this is the hardware interrupt handler (registered viabus_setup_intr:418-420, serializer passed).bfe_init(:1355-1387) βbfe_list_rx_init(sc):1368.bfe_list_rx_init(:538-554) loopsbfe_newbuf(sc, i, 1)βinit=1.bfe_newbuf(:557-595):m = m_getcl(init ? M_WAITOK : M_NOWAIT, MT_DATA, M_PKTHDR):565 β M_WAITOK in ithread context.- On
bus_dmamap_load_mbuf_segmentfailure:if_printf:576 (console output from interrupt context β tolerated but noisy).
Sibling path: bfe_watchdog :1470-1483 β bfe_init :1480 runs from
if_slowtimo_dispatch (netisr, sys/net/if.c:1900-1912) β also
non-sleepable.
Why it matters (threat)
BFE_ISTAT_RDU (receive descriptor underflow) is raised when the driver
runs out of posted RX buffers β i.e. under cluster shortage. The recovery
path then demands 511 new clusters with M_WAITOK from the ithread. Under
the same shortage the allocator sleeps; the ithread (holding the
serializer, pinning its IRQ) sleeps with it. Until clusters free up
(possible only if other CPUs/network progress releases them) the NIC and
its interrupt line are dead; if nothing frees clusters (all freeing paths
blocked behind the same serializer), the stall is permanent. Realistic but
narrow: Low severity.
Negative results (adjacent, this file)
bfe_initfrombfe_ioctl(SIOCSIFFLAGS :1448) is process context β M_WAITOK fine there.bfe_list_rx_initerror path (== ENOBUFS:1368-1373) stops and callsbfe_stopβ clean.- The map-swap in
bfe_newbuf(:583-588) is correct on both load-success and retained-mbuf paths.
Fix validation
fix.diff plumbs waitok (intr/watchdog pass 0 β M_NOWAIT; process paths
keep M_WAITOK), patch -p1 --dry-run clean. Cannot be runtime-validated
without hardware β fix_status not_testable.
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 against the read-only sys/ tree (patch -p1 --dry-run clean); cannot be runtime-validated without BCM4401 hardware.
findings/poc/DF-4305/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (path:line trace incl. the RDU self-reinforcement argument and the netisr watchdog sibling); env.txt (no Broadcom PCI, hw.bfe unknown oid, guest src md5 == audited tree); fix.diff (waitok plumbing, patch -p1 --dry-run clean)
PoC changes
No seed; no runnable PoC exists without BCM4401 hardware - pack documents the source proof, the trigger analysis, and the fix.
Verified recommended fix
Plumb a waitok flag through bfe_init/bfe_list_rx_init/bfe_newbuf and pass 0 (M_NOWAIT) from the interrupt (bfe_intr :1186) and watchdog (:1480) reinit paths; see fix.diff.
Verdict
Source-certain sleepable-allocation defect: the bfe_intr error path (sys/dev/netif/bfe/if_bfe.c:1176-1187) calls bfe_init at :1186 from interrupt context; bfe_init -> bfe_list_rx_init (:1368) -> bfe_newbuf(sc, i, 1) -> m_getcl(M_WAITOK) at :565 allocates 511 clusters sleepably inside the ithread while it holds the interface serializer, and the load-failure branch calls if_printf (:576). BFE_ISTAT_RDU (receive descriptor underflow, if_bfereg.h:32) - the most likely error to fire - occurs exactly under cluster exhaustion, making the M_WAITOK re-arm sleep in the interrupt thread precisely when the system cannot satisfy it: the NIC's IRQ line and every serializer-guarded entry point stall until (unless) clusters free up elsewhere. The TX-watchdog reinit (bfe_watchdog :1470-1483 -> bfe_init :1480, netisr context) is equally non-sleepable and hits the same path. Kernel-path reproduction is impossible on this guest: no Broadcom BCM4401/4402 device exists and QEMU provides no model (env.txt: NO_BROADCOM_PCI, hw.bfe unknown oid); verdict not_reproduced with the defect certain from the source trace, per family convention (DF-3827). Impact ceiling: interface/interrupt-line stall (soft deadlock) under memory pressure - availability only, no memory corruption.
No comments yet.