# DF-0581 — ieee80211_setup_rates trusts attacker-controlled IE length

## Bug
`ieee80211_setup_rates` (sys/netproto/802_11/wlan/ieee80211_input.c:413)
copies a TLV rateset from a received management frame into a fixed-size
struct:

```c
struct ieee80211_rateset *rs = &ni->ni_rates;
memset(rs, 0, sizeof(*rs));
rs->rs_nrates = rates[1];                    /* line 420 -- attacker-controlled */
memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);  /* line 421 */
```

`struct ieee80211_rateset` (sys/netproto/802_11/_ieee80211.h:375-380):
```c
#define IEEE80211_RATE_MAXSIZE  15
struct ieee80211_rateset {
    uint8_t  rs_nrates;
    uint8_t  rs_rates[IEEE80211_RATE_MAXSIZE];  /* 15 bytes */
};
```

`rates[1]` is the IE length byte (0..255), and the function trusts it
without bounds check. A rateset IE with length > 15 (legitimate max)
causes the `memcpy` to write up to 240 bytes past `rs_rates[15]`,
overflowing into whatever `struct ieee80211_node` fields follow
`ni_rates`.

The xrates handling at lines 427-436 IS correctly bounded (clamps to
IEEE80211_RATE_MAXSIZE).

## Mitigations (in callers)
All beacon/probe-response parsing callers go through
`IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, action)`
(sys/netproto/802_11/ieee80211_input.h:31) BEFORE calling
`ieee80211_setup_rates`. So on production paths, the rates IE is
already length-validated.

However:
- `ieee80211_node.c:856` (the `ieee80211_setup_rates` call from the
  neighbor join) takes data from the scan cache, which on INVARIANTS
  kernels is `KASSERT`-checked at `ieee80211_scan_sta.c:285-287`, but
  on production kernels the KASSERT is a no-op and bad data flows
  through.
- The function itself has NO bounds check, so any new caller (or a
  regression in the existing KASSERT) re-opens the OOB write.
- On default GENERIC (#0 baseline, INVARIANTS ON), the
  `KASSERT(sp->rates[1] <= IEEE80211_RATE_MAXSIZE, ...)` at
  ieee80211_scan_sta.c:285 fires FIRST and panics the kernel — making
  this bug a DoS via panic on default kernels, and an OOB write on
  production/no-INVARIANTS kernels.

## Reachability on this guest
Requires a WiFi interface in monitor/managed/hostap mode receiving
frames. The guest has NO WiFi hardware (no `wlan`/`ath`/`iwm`/etc.
drivers loaded; QEMU has no WiFi device). Therefore the bug cannot be
triggered at runtime on this guest.

The bug is real and traced line-by-line into source. It is a latent
defect that manifests on any DFly system with a WiFi interface under
adversarial RF input.

## Fix
Bound the length at the function itself (defense in depth, since the
function is the actual sink). See fix.diff.
