# DF-3283 — ieee80211_fragment: fragment size can exceed MCLBYTES → m_align() negative offset → heap underflow write (release) / KASSERT panic (INVARIANTS)

File: `sys/netproto/802_11/wlan/ieee80211_output.c` (pass 2 audit)

## What

`ieee80211_fragment()` (lines 1773-1877) computes per-fragment
`fragsize = min(totalhdrsize + remainder, mtu)` where `mtu =
vap->iv_fragthreshold`, user-configurable via
`IEEE80211_IOC_FRAGTHRESHOLD` to `[IEEE80211_FRAG_MIN(256),
IEEE80211_FRAG_MAX(2346)]` (ieee80211_ioctl.c:3081-3089) — and the
**default** is FRAG_MAX=2346 (ieee80211_var.h:85). Each fragment is
built in a single mbuf cluster:

```c
/* XXX fragsize can be >2048! */                    <- developer's own note
KASSERT(fragsize < MCLBYTES, ...);
if (fragsize > MHLEN)
        m = m_getcl(...);                           /* 2048-byte cluster */
m_align(m, fragsize - ciphdrsize);                  /* 2338 > 2048 */
```

`m_align()` (sys/kern/uipc_mbuf.c:1935) computes
`adjust = ext_size - len = 2048 - 2338 = -290`, rounds to -288, and does
`m_data += adjust` — **m_data lands 288 bytes *before* the cluster**. The
following `memcpy(whf, wh, hdrsize)` + `m_copydata(m0, off, payload, ...)`
then write ~2346 bytes straddling the cluster: ~262 bytes of
attacker-supplied payload *before* the cluster and 2 bytes past its end.
On INVARIANTS kernels the KASSERT turns this into a panic instead.

Bug triggers whenever a frame of ≥ ~4.1 KB reaches `ieee80211_encap()`
on a TXFRAG-capable driver (fragthreshold²-scale). Unprivileged reach is
currently blocked only because `SIOCSIFMTU` clamps wlan MTU to
IEEE80211_MTU_MAX=2290 (ieee80211_ioctl.c:3481-3487), keeping stack
frames < 2346+2022; privileged callers bypass that gate trivially
(BPF write to the wlan ifnet, or any in-kernel caller of the exported
`ieee80211_vap_pkt_send_dest()`). This is latent memory corruption whose
only guard today is an assert compiled out of release kernels.

## Build / run (repo root)

```
sh findings/poc/DF-3283/build.sh     # builds /root/fw/fw.ko in guest
sh findings/poc/DF-3283/run.sh       # baseline: panic
```

The harness fw.c advertises `IEEE80211_C_TXFRAG` (as ath/wpi/rt2560/
rt2661 do), brings up a hostap vap (beacon path proven in DF-3282), then
`sysctl kern.fw_fire=1` pushes one 5000-byte 802.3 frame through the
real exported TX entry `ieee80211_vap_pkt_send_dest()`.

## Expected output

Baseline (stock INVARIANTS kernel):

```
fw.fire: vap wlan0, pushing 5000 byte frame (fragthreshold 2346)
panic: fragment size 2346 too big!
ieee80211_encap() at ieee80211_encap+0xb01
ieee80211_vap_pkt_send_dest() at ...
```

Patched (fix.diff clamps mtu to MCLBYTES): "fw.fire: returned (no
panic)" — frame fragmented into ≤2048-byte fragments, guest stays up.

## Threat

Privileged local memory corruption / panic on TXFRAG drivers. The write
is packet-data controlled (heap underflow before an mbuf cluster +
overflow past its end), so on release kernels it is a classic
slab-corruption primitive; the trigger length needs only a >4.1 KB mbuf
chain, which ifnet-MTU accounting no longer enforces for BPF/in-kernel
callers.
