Unbounded device-register polling under BWN_LOCK in bwn_intr_pmq and bwn_core_start: permanent unkillable wlan-subsystem wedge
Summary
bwn_intr_pmq and bwn_core_start poll device registers in while(1) with no retry bound while holding sc_lk exclusive. A stalled/failing card (or malicious one freezing PS_STATUS/XMITSTAT) spins the bwn taskq thread forever with the lock held: every later bwn ioctl/start/stop/detach blocks unkillably in lockmgr and the watchdog callout (which shares sc_lk) dies. Every other polling loop in the driver is bounded - these two are the outliers. Permanent soft-hang DoS of the wlan subsystem on stalled hardware; not remote; requires device misbehavior. Source-certain; guest hardware-absent.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3596 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 774 B | β raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| probe.sh | β | 838 B | view raw | |
| run.log | β | 581 B | view raw | |
| fix.diff | β | 973 B | view raw | |
| manifest.json | β | 929 B | view raw | |
| verdict.json | β | 1.9 KB | view raw |
DF-3596 β Unbounded device-register polling loops under BWN_LOCK (bwn_intr_pmq, bwn_core_start): permanent lock-held wedge on stalled device
Build / Run / Expected
# on the DF guest, as root: sh probe.sh
Guest result: driver resident, no Broadcom hardware β runtime inconclusive.
On a BCM43xx machine with a wedged/failing card (or malicious card that simply
stops updating the PS_STATUS / XMITSTAT registers): the next PMQ interrupt or
core-start spins forever reading the frozen register while holding sc_lk
exclusive β the bwn taskq thread pegs a CPU, every subsequent ioctl/start/stop
on the interface blocks forever in lockmgr (unkillable), and the device can
no longer be reset or detached. System-wide soft hang of the wlan subsystem.
VERDICT β DF-3596
Status: inconclusive at runtime (no bwn hardware on the audit guest); defect source-certain. Impact: DoS β unkillable lock-held kernel spin (wlan subsystem wedge) on stalled device.
Root cause (path:line)
Two while (1) loops poll device registers with no iteration bound and no
timeout, both executing with BWN_LOCK held (sc_lk, lockmgr exclusive β
if_bwnvar.h:1030-1035):
bwn_intr_pmq()βsys/dev/netif/bwn/bwn/if_bwn.c:5168-5178:c while (1) { tmp = BWN_READ_4(mac, BWN_PS_STATUS); if (!(tmp & 0x00000008)) break; }Called frombwn_intrtask(:4995-4996) on everyBWN_INTR_PMQ(part ofBWN_INTR_MASKTEMPLATE,if_bwnreg.h:326), i.e. straight from the device's power-management-queue interrupt, under the lock taken at:4954.bwn_core_start()βif_bwn.c:2234-2239:c while (1) { tmp = BWN_READ_4(mac, BWN_XMITSTAT_0); if (!(tmp & 0x00000001)) break; tmp = BWN_READ_4(mac, BWN_XMITSTAT_1); }Reached frombwn_init/bwn_hwreset/bwn_switch_bandwith the lock held.
Every other polling loop in this driver is bounded (bwn_dma_tx_reset
10 iterations :3307, bwn_mac_suspend 35+40 :3836-3847, bwn_psctl 100
:3898, PIO rxeof 10 :5567, ucode-timeout loop 21 :4263) β these two are
the outliers, and the upstream Linux b43 equivalent (b43_pio_tx /
ssb-wait helpers) uses bounded waits.
Impact when a card stalls (failing hardware is the classic trigger; a
malicious mini-PCIe card can freeze the register deliberately): the loop never
exits; the bwn taskq thread spins at 100% holding sc_lk forever. Every later
ifconfig bwn0 β¦, TX wakeup (bwn_start), detach, or suspend blocks
unkillably on the lock; the watchdog callout (sc_watchdog_ch shares sc_lk,
if_bwn.c:844) also blocks, so even the device-timeout printer dies. Not a
panic β a permanent, unkillable subsystem wedge.
Why runtime reproduction was not possible
Same as the sibling findings: the audit guest has no Broadcom 802.11 device
(run.log); the loop bodies are unreachable. A qemu-specific device model
would be required to emulate a stalling bwn card.
Fix
Bound both loops with a retry budget and bail out with a device_printf on
timeout β see fix.diff.
Artifacts
probe.sh,run.logβ guest probe (driver residency + hardware absence)fix.diffβ proposed patch vs read-onlysys/tree (never applied)
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 read-only sys/; cannot baseline-validate without BCM43xx hardware
fix.diff in this pack
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md loop-by-loop analysis with bounded-loop contrast; run.log guest probe; fix.diff adds retry budgets + timeout messages
PoC changes
No runnable PoC possible without hardware; probe.sh substitutes
Verified recommended fix
Bound both register-drain loops with retry budgets and timeout diagnostics
Verdict
Source-certain unbounded polling: bwn_intr_pmq (if_bwn.c:5172-5176) and bwn_core_start (if_bwn.c:2234-2239) spin on device registers with no retry bound while holding sc_lk (lockmgr exclusive); a stalled/failing card (or malicious mini-PCIe device) freezes the thread with the lock held, wedging every later bwn ioctl/start/stop/detach and the watchdog callout permanently and unkillably. All other polling loops in the driver are bounded (e.g. :3307, :3836, :3898, :5567) β these two are the outliers. Guest has no Broadcom device (run.log), so the loop bodies are unreachable at runtime.
No comments yet.