# DF-1131 — bwn DMA rxeof dr_rx_bufsize / cluster-size mismatch OOB read

## Verdict
**CONFIRMED (source-trace + harness) — INCONCLUSIVE on-guest (HW-gated).** The bug
is real; it cannot be triggered on the audit QEMU guest because the guest has no
Broadcom BCM43xx (bwn) WiFi adapter (virtio net only).

## Bug (one line)
`bwn_dma_rxeof()` checks `frame_len` against `dr->dr_rx_bufsize` (=
`BWN_DMA0_RX_BUFFERSIZE` = `IEEE80211_MAX_LEN` = 2312) but the RX mbuf cluster is
only `MCLBYTES` (2048), so a device-reported `frame_len` in (2018, 2312] passes the
check yet makes `m_len = len + dr_frameoffset` exceed the 2048-byte cluster.

## Mechanism (path:line)
- `if_bwn.c:2778` — `dr->dr_rx_bufsize = BWN_DMA0_RX_BUFFERSIZE;`
  (`BWN_DMA0_RX_BUFFERSIZE = IEEE80211_MAX_LEN`; `if_bwnreg.h:464`).
- `IEEE80211_MAX_LEN = 2300 + 4 + (3+1+4) = 2312` (`ieee80211.h:1297`); `MCLBYTES = 2048`.
- `if_bwn.c:5674` — `m = m_getcl(...)` allocates an **MCLBYTES (2048)** cluster.
- `if_bwn.c:5688` — `m->m_len = m->m_pkthdr.len = MCLBYTES;`
- `if_bwn.c:5470` — `len = le16toh(rxhdr->frame_len);` (device-reported).
- `if_bwn.c:5486` — `if (len > dr->dr_rx_bufsize)` checks against **2312**, not 2048.
- `if_bwn.c:5512` — `m->m_len = m->m_pkthdr.len = len + dr->dr_frameoffset;` (frameoffset=30)
  exceeds 2048 for `len > 2018`, but the cluster is only 2048 -> net80211 reads
  past the cluster when processing the frame.

## Trigger / threat model
A malicious/rogue 802.11 frame (or a firmware bug) that makes the device report
`frame_len` in (2018, 2312] is kept (it would be dropped only above 2312) and
processed with an `m_len` larger than the cluster. net80211 then over-reads up to
`(2312+30-2048)=294` bytes of adjacent kernel heap; those bytes appear in the
delivered frame payload (info leak) or hit an unmapped page (panic). Requires a
Broadcom bwn adapter; attacker is within radio range.

## Reproduction on the audit guest
Not possible — no bwn WiFi hardware. `harness.c` is a userspace replica of the
length-check arithmetic, built and run as unprivileged `maxx`, proving the OOB and
that the fix (drop when `len + frameoffset > MCLBYTES`) yields 0 OOB.

## Build / run
```
./build.sh && ./run.sh
```
Expected: `DF-1131: CONFIRMED heap OOB read primitive (up to 294 bytes past cluster)`
then `DF-1131 FIX: VALIDATED - cluster-bound guard drops all overrun frames`.

## Fix
`fix.diff` adds `(u_int)len + dr->dr_frameoffset > MCLBYTES` to the existing drop
check. Applies cleanly; compiles in the GENERIC kernel build (rc=0).
