# DF-3363 VERDICT

**Status: reproduced** (baseline, stock INVARIANTS kernel #0,
DragonFly 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026).
**Fix: validated** (kernel #2, Fri Sep 11 12:44:59 UTC 2026).

## How it was reproduced

The `fwscan.k` LD software radio (adapted from the DF-3282 harness)
injects fully attacker-controlled beacons through the exact production
path a driver uses: `ieee80211_parse_beacon()` →
`ieee80211_add_scan()` → `sta_add()` → `ieee80211_ies_init()` (verbatim
64-byte blob), then `ss_ops->scan_end()` → `sta_pick_bss()` →
`select_bss()` → `ieee80211_ies_expand()` → `match_bss()` /
`sta_compare()` → **`maxrate()`** — the victim read.

Decisive observations (stock kernel):

1. `entry mac 02:22:22.. blob ..d240 len 64` and its groom neighbor at
   `..d280` — exactly +0x40. `htcap_ie: off 62, ie_len 0` → every byte
   maxrate reads (IE+2..IE+17 = blob[64..79]) is past the allocation.
2. With 0xff beyond the blob: `iv_bss mac 02:22:22:22:22:22` — the
   5.5 Mbps AP beats the real 54 Mbps AP.
3. With 0x00 beyond the blob (identical beacons): `iv_bss mac
   02:11:11:11:11:11`. With a valid zero HTCAP: A wins.
4. Post-fix (fix.diff, kernel #2): variant 1 → A wins; the OOB-derived
   selection is gone; valid IE traffic (TDMA v17) still accepted.

## Why it happens (path:line)

- ieee80211_scan_sta.c:380 — saves raw IE blob (`sp->ies`, `sp->ies_len`).
- ieee80211_node.c:1009-1010 — `ies->htcap_ie = ie` with no length check.
- ieee80211_scan_sta.c:787,797,799 — `hc_mcsset[12]` (off 17),
  `isclr(hc_mcsset, i)` (off 5-8), `le16dec(hc_cap)` (off 2-3).
- ieee80211_input.c:733-739 — parse-time HTCAP validation only NULLs
  `sp->htcap`, unused by sta_add.

## Exploit chain (as far as the class permits)

Remote-unauth beacon → OOB read of ≤16 heap bytes past a small
kmalloc → values steer `sta_compare` → attacker-visible effect (which
BSS the station joins). It is an information **oracle** on adjacent
heap content (e.g. "is the byte at blob_end+15 nonzero"), not a
disclosure channel or write primitive; no uid0 route exists for a
read-only, oracle-grade primitive. Attempts: multiple groom
strategies (LIFO-reuse, D-neighbor, post-spray) until deterministic.

## Notes on PoC repairs vs the seed

The harness was written fresh for this finding (no seed existed).
Iterations that mattered: vap creation MUST go through the ifconfig
clone path (in-kernel `ic_vap_create` from a sysctl deadlocks —
serializer discipline); the fake radio needs exactly one channel so
`ic_curchan` never moves under DSPARMS; kmod `opt_wlan.h` generation
is broken on this bsd.kmod.mk (empty file) so the TDMA/MESH defines
live in the source; background scans bump `se_notseen` → candidates
are re-injected immediately before `scan_end`; `scan_signal()` asserts
the ic lock is NOT held, so the lock is taken only around `scan_end`.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
