Duplicate firmware TX-done re-completes a freed TX slot: NULL-deref/KASSERT panic and TX-ring accounting underflow
Summary
bwn_dma_handle_txeof/bwn_pio_handle_txeof map a firmware TX-done cookie to a slot/txpkt with only a range check - no completion-state validation. A duplicate TX-done (behavior the driver's own comment documents occurring in the field) re-enters completion for an already-freed descriptor pair: KASSERT(meta->mt_m!=NULL) panics INVARIANTS kernels (stock X86_64_GENERIC); non-INVARIANTS builds NULL-deref at meta->mt_ni->ni_vap or, in PIO mode, at tp->tp_m->m_pkthdr.len. dr_usedslot is also decremented twice, corrupting free-slot accounting. Any BCM43xx-equipped DragonFly system running stock kernel: a firmware duplicate TX-done instantly panics the kernel; a malicious mini-PCIe card can synthesize cookies at will. Local DoS; no uid0 chain - the corrupted value stays a range-checked slot index. Hardware-absent guest: source-certain, inconclusive at runtime.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3593 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| probe.sh | β | 838 B | view raw | |
| run.log | β | 581 B | view raw | |
| fix.diff | β | 1.5 KB | view raw | |
| manifest.json | β | 1.1 KB | view raw | |
| verdict.json | β | 2.4 KB | view raw |
DF-3593 β Duplicate firmware TX-done re-completes a freed TX slot (NULL-deref / KASSERT panic + ring accounting underflow)
Build / Run / Expected
No runtime trigger exists on the audit guest (virtio-only, no Broadcom card):
# on the DF guest, as root: sh probe.sh
Expected output (what we got): driver resident (hw.bwn.* sysctls live, compiled into
X86_64_GENERIC), INVARIANTS enabled, NO Broadcom wireless PCI device β the
TX-completion interrupt path cannot execute. Verification is therefore
source-level certain; runtime classification is inconclusive (hardware absent).
On a machine with a BCM43xx card whose firmware emits a duplicate TX-done event
(the driver's own comment, if_bwn.c:7088-7091, states this happens in the
field), the expected observation is an immediate kernel panic:
- INVARIANTS kernels (stock DF
X86_64_GENERIC,sys/config/X86_64_GENERIC:56):panic: kassert "/sys/dev/netif/bwn/bwn/if_bwn.c:5996: fail"(DMA mode) orif_bwn.c:6021: fail. - non-INVARIANTS kernels: NULL-pointer dereference fatal page fault in
bwn_dma_handle_txeof(meta->mt_ni->ni_vap,if_bwn.c:6012) orbwn_pio_handle_txeof(tp->tp_m->m_pkthdr.len,if_bwn.c:6052).
Trigger precondition
- bwn(4) attach of a BCM43xx device (DMA or PIO mode β PIO via PCMCIA or
hw.bwn.usedma=0). - Firmware raises
BWN_INTR_TX_OKa second time for a cookie whose slot pair was already completed β i.e. twobwn_intr_txeofiterations delivering the same cookie. The in-tree comment atif_bwn.c:7088-7091documents exactly this hardware behavior ("sometimes H/W returns TX DONE events duplicately"). A malicious/compromised mini-PCIe card can synthesize it at will.
VERDICT β DF-3593
Status: inconclusive at runtime (no bwn hardware on the audit guest); the defect itself is source-certain.
Impact: kernel panic (local DoS) β KASSERT panic on INVARIANTS kernels, NULL-deref fatal trap on non-INVARIANTS builds; plus TX-ring accounting corruption (dr_usedslot underflow).
Root cause (path:line)
TX completion maps a firmware-supplied 16-bit cookie back to the originating slot/txpkt, but never validates that the slot is still outstanding:
bwn_intr_txeof()(sys/dev/netif/bwn/bwn/if_bwn.c:5296-5336) pops everyBWN_XMITSTATentry;stat.cookie = stat0 >> 16(:5308) is raw firmware data.bwn_dma_parse_cookie()(:7055-7102) decodes queue (cookie & 0xf000) and slot (cookie & 0x0fff). The only rejection is the range check:7087(*slot >= dr->dr_numslots, 256 slots β pass-1 DF-1132 covers the separatedr == NULLderef when the queue nibble is invalid). A duplicate cookie for a valid, already-completed slot passes all checks.- First completion (
bwn_dma_handle_txeof,:5984-6028) walks HEADERβBODY slots, and at the BODY slot does:ieee80211_ratectl_tx_complete(meta->mt_ni->ni_vap, β¦)(:6012),ieee80211_tx_complete(meta->mt_ni, meta->mt_m, 0)(:6017), then clearsmeta->mt_ni = NULL; meta->mt_m = NULL;(:6018-6019) and decrementsdr->dr_usedslot(:6024). - A second TX-done with the same cookie re-enters: HEADER slot unloads its
(already unloaded) map, BODY slot has
mt_islast == 1: *KASSERT(meta->mt_m != NULL)(:5996) β panics every INVARIANTS kernel (stockX86_64_GENERICbuilds withoptions INVARIANTS,sys/config/X86_64_GENERIC:56); * non-INVARIANTS:meta->mt_ni->ni_vapat:6012withmt_ni == NULLβ NULL-deref fatal page fault in kernel mode; even before that,ieee80211_tx_complete(NULL, NULL, 0)at:6017would trap. dr->dr_usedslot--executes a second time (:6024) β underflow below zero βbwn_dma_freeslot()(:5421-5426,numslots - usedslot) now reports more free slots than the ring holds; TX path (bwn_dma_tx_start :1151bwn_dma_getslot) then advancesdr_curslotonto descriptors whose meta state is inconsistent.- PIO mode is worse:
bwn_pio_parse_cookie()(:6726-6760) only validates the index againstN(tq->tq_pkts);bwn_pio_handle_txeof()immediately doesroundup(tp->tp_m->m_pkthdr.len + BWN_HDRSIZE(mac), 4)(:6052) βtp->tp_mis NULL for any already-completed (free-listed) packet β NULL deref on the first instruction of the completion.
The driver itself documents that the trigger occurs in the field β
if_bwn.c:7088-7091:
XXX FIXME: sometimes H/W returns TX DONE events duplicately that it occurs events which have same H/W sequence numbers.
so this is not merely a malicious-peripheral path; it is a known device
behavior with no completion-state guard. (The seq guard KASSERT(status->seq
== dma->lastseq) at :7093 lives inside the out-of-range branch only and
duplicates carry the same seq, so it never fires.)
Why runtime reproduction was not possible
The audit guest is QEMU/KVM with virtio devices only. probe.sh / run.log
show: hw.bwn.* sysctls live (driver compiled into the kernel,
sys/config/X86_64_GENERIC:267-268), and pciconf -l shows no Broadcom
802.11 device (no class=028000, no chip=β¦14e4). The interrupt/TX-done
path is unreachable without the card. No uid=0 chain is applicable β this is
a device-driven crash, not a memory-corruption primitive under attacker-chosen
control (the corrupted quantity is a slot index that stays range-checked).
Fix
Guard completion state before use (both engines), mirroring what the range
check already does for slot indices β see fix.diff in this directory.
Artifacts
probe.shβ guest probe (driver residency + hardware absence)run.logβ probe output (untrimmed)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 on the guest
fix.diff in this pack
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md full path:line trace; run.log = guest probe (driver resident, hw absent); fix.diff guards duplicate completion state in both DMA and PIO engines
PoC changes
No runnable PoC possible without hardware; probe.sh substitutes, proving residency+absence
Verified recommended fix
Peek the BODY descriptor (mt_m == NULL) / PIO tp_m == NULL before completing; drop duplicate TX-done statuses
Verdict
Source-certain NULL-deref/KASSERT panic on duplicate firmware TX-done; the driver's own comment (if_bwn.c:7088-7091) admits duplicates occur in the field. Runtime trigger impossible on the audit guest: virtio-only QEMU box has no Broadcom 802.11 PCI device (see run.log), and the driver is compiled into the stock kernel (X86_64_GENERIC:267-268) with INVARIANTS (:56), so any BCM43xx-equipped stock DF system panics at KASSERT if_bwn.c:5996; non-INVARIANTS builds NULL-deref at if_bwn.c:6012 (meta->mt_ni->ni_vap) or if_bwn.c:6052 (tp->tp_m->m_pkthdr.len). Secondary effect: dr_usedslot double-decrement (if_bwn.c:6024) corrupts TX-ring free-slot accounting (bwn_dma_freeslot at :5425).
No comments yet.