# DF-2358 — Heap buffer overflow in USB RX frame aggregation (sys/bus/u4b/wlan/if_run.c)

## Verdict: NOT REPRODUCED (HW-gated); REAL BUG IN SOURCE (defense-in-depth fix warranted)

## Hardware gate (why the PoC cannot run on this guest)

`run` is the Ralink RT2700U/RT2800U/RT3000U/RT3900E 802.11abgn USB wifi driver.
It attaches only when a matching Ralink USB wifi dongle is plugged in. The audit
QEMU/KVM guest has **no USB device and no wifi interface**:

```
$ usbconfig list               # No device match or lack of permissions.
$ ifconfig -l                  # vtnet0 lo0   (no wlan/run)
$ pciconf -l | grep -iE "ralink|148f|1435|0789"   # (no Ralink USB wifi chip)
$ kldstat                      # kernel + ehci.ko + xhci.ko only
```

The RX aggregation path `run_bulk_rx_callback` runs only when the `run` driver
has attached to a Ralink USB wifi device and is receiving batched frames. With no
such device the path never executes; the unprivileged `maxx` user cannot plug a
USB dongle into the QEMU guest, and there is no adjacent rogue-AP threat (no
radio).

## Source trace — the bug is REAL (sys/bus/u4b/wlan/if_run.c)

`run_bulk_rx_callback` (`if_run.c:2897-...`) splits an aggregated USB bulk
transfer into individual 802.11 frames. Bounds (`if_runvar.h:26-27`):
```c
#define RUN_MAX_RXSZ  MIN(4096, MJUMPAGESIZE)   /* 4096 */
```
The source buffer `sc->rx_m` is correctly allocated with `m_getcl`/`m_getjcl`
(`MJUMPAGESIZE` = 4096), so the *source* of the copy is in bounds. The problem
is the *destination*:

```c
m->m_pkthdr.len = m->m_len = xferlen;                       /* if_run.c:2985 */
for(;;) {
    dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff;        /* device-controlled 16-bit */
    if ((dmalen >= (uint32_t)-8) || (dmalen == 0) || ((dmalen & 3) != 0)) break;
    if ((dmalen + 8) > (uint32_t)xferlen) break;            /* if_run.c:2996: bound by xferlen, not dest */
    if ((xferlen -= dmalen + 8) <= 8) { ... run_rx_frame(sc, m, dmalen); break; }  /* final frame uses 4096-byte src buf */
    /* aggregated (non-final) frames: copy into a NEW m_getcl cluster */
    m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);              /* if_run.c:3013: cluster = MCLBYTES = 2048 */
    ...
    m_copydata(m, 4, dmalen + sizeof(struct rt2870_rxd),    /* if_run.c:3023 */
               mtod(m0, void *));                           /* writes dmalen+rxd_size into 2048-byte cluster */
```

`dmalen` is bounded only by `xferlen` (≤ `RUN_MAX_RXSZ` = 4096), so for a
non-final aggregated frame `dmalen + sizeof(struct rt2870_rxd)` can reach ~4080
bytes — but `m0` is a `m_getcl` cluster of only **MCLBYTES = 2048** bytes.
`m_copydata` does not bounds-check the destination (the `KASSERT` at
`kern/uipc_mbuf.c:1671` validates only the *source*), so the copy overflows the
2048-byte cluster by up to ~2032 bytes into adjacent slab memory.

Two attack vectors: (1) wireless adjacent-network rogue AP bursts large data
frames the firmware aggregates; (2) a malicious USB device impersonating Ralink
sends a crafted bulk IN. Impact: kernel heap corruption → arbitrary kernel code
execution or panic.

## Exploit chain status

Not pursuable — primitive (heap overflow of destination cluster) requires a
Ralink USB wifi dongle or adjacent radio (absent) — valid Phase-6 hard blocker:
dead path at runtime on this guest. On hardware this is a write-capable heap
overflow.

## PoC changes

None. No Ralink USB wifi dongle on guest; verified by source trace only.

## Recommended fix

Use a destination mbuf large enough for `dmalen + sizeof(rt2870_rxd)` (e.g.
`m_getjcl(MJUMPAGESIZE)`) or bound `dmalen` against `MCLBYTES -
sizeof(rt2870_rxd)`. See `fix.diff` (matches finding proposal: allocate the
destination with a cluster that can hold the maximum possible copy).
