# DF-0291 — heap overflow in `setmlme_assoc_adhoc` (SIOCS80211 MLME ASSOC adhoc)

## Claim (from the DB finding row, High / CWE-787 / confidence `certain`)

`setmlme_assoc_adhoc` (`sys/netproto/802_11/wlan/ieee80211_ioctl.c:1568`) only
checks `ssid_len == 0` at `:1580`, **not** `ssid_len > IEEE80211_NWID_LEN (32)`.
It then `memcpy`s an attacker-controlled length (up to 255, since
`struct ieee80211req_mlme.im_ssid_len` is `uint8_t`) into the 32-byte
`vap->iv_des_ssid[0].ssid` (`:1595`) and `sr->sr_ssid[0].ssid` (`:1600`) →
~223-byte heap overflow into `struct ieee80211vap` / the `kmalloc`'d
`struct ieee80211_scan_req`. The sibling `IEEE80211_IOC_SSID` SET handler
(`:2671`) **does** bound `ireq->i_len` at `:2673`, so the adhoc path is the only
unguarded sink. Amplification: the GET `IEEE80211_IOC_SSID` handler (`:805`)
reads the corrupted `iv_des_ssid[0].len` at `:809` and `memcpy`s it into a
32-byte **stack** buffer `tmpssid` at `:810` → stack overflow. WiFi-capability
gated (root / `SYSCAP_NONET_WIFI`).

## Verdict

**INCONCLUSIVE — real shipped bug, unreachable on this audit guest.**

The overflow is **real and confirmed line-by-line** in the audited master DEV
source (`overflow_proof.c` reproduces the exact vulnerable logic and prints the
byte counts). It is **not reachable from userspace on the KVM guest** because
the `SIOCS80211`/`SIOCG80211` ioctls are wired only to **wlan vap** interfaces,
and a wlan vap can only be created through `wlan_clone_create`
(`ieee80211_dragonfly.c:80`), which **requires a wifi radio parent** and returns
`ENXIO` when none is registered (`:92-94`). The guest has `vtnet0`/`lo0` only,
no radio hardware, and no attached radio driver, so no vap can ever exist and
the ioctl is never delivered. See `VERDICT.md` for the full trace and
`env.txt` for the live reachability probe.

## Files in this evidence pack

| file | what it is |
|------|------------|
| `overflow_proof.c` | userspace MODEL of the kernel path — demonstrates the heap + stack overflow byte counts concretely |
| `build.sh` / `run.sh` | exact build & run |
| `build.log` / `run.log` | full untrimmed build & run output |
| `env.txt` | guest uname, cc, kldstat, wlan-symbol check, and the `ifconfig wlan create` reachability probe |
| `VERDICT.md` | full narrative: code-level proof + reachability classification |
| `fix.diff` | `git apply`-able fix for BOTH the set-path overflow and the GET-path stack overflow |
| `manifest.json` | machine-readable catalog |

## Reproduce

```sh
./build.sh && ./run.sh      # prints the heap (223B past 32B) + stack (223B past 32B) overflow counts
```

To reach the in-kernel sink you need a host with a wifi radio + driver that
registers an `ieee80211com`, create a vap (`ifconfig wlan create wlandev <radio>
wlanmode adhoc`), then issue `SIOCS80211` with `im_op=IEEE80211_MLME_ASSOC` and
`im_ssid_len=255`. That hardware is absent on this guest, so no in-kernel
trigger was attempted.

## Privilege

The SET path is gated by `caps_priv_check_self(SYSCAP_NONET_WIFI)` at
`ieee80211_ioctl.c:3472` (root-equivalent). So even with a radio present this is
a **local privileged → kernel memory corruption** primitive, not unprivileged.
On a wifi-equipped embedded/AP box where the radio is auto-configured and a
semi-trusted context holds the wifi capability, it becomes a real concern.
