# DF-3460 — VERDICT

**Status: REPRODUCED** (code-level harness, latent module on this guest)
**Impact: leak** (unprivileged 4-byte kernel-heap UAF read via world-readable sysctl
during vap teardown; root 4-byte UAF write in the same window)
**Confidence: certain** (teardown order proven from source; harness run on the audit guest)

## The window is real — source trace

```
iwm_vap_delete()                       if_iwm.c:6486-6493
 └─ ieee80211_ratectl_deinit(vap)      ieee80211_ratectl.h:69-72
     └─ rssadapt_deinit(vap)           ieee80211_rssadapt.c:151-155
         └─ IEEE80211_FREE(vap->iv_rs) … iv_rs NOT cleared
 └─ ieee80211_vap_detach(vap)          ieee80211.c:752…
     ├─ ether_ifdetach / ieee80211_stop / draintask / …
     ├─ ieee80211_node_vdetach         ieee80211.c:826
     └─ ieee80211_sysctl_vdetach(vap)  ieee80211.c:827   <- oid removed only HERE
```

Everything between the `IEEE80211_FREE` and `ieee80211_sysctl_vdetach` — interface
detach, state stop, task drains, node table teardown — is the window in which the
oid `net.wlan.N.rssadapt_rate_interval` remains registered (world-readable,
CTLFLAG_RW) while its handler (`rssadapt_sysctl_interval`,
ieee80211_rssadapt.c:340-353) dereferences the dangling `vap->iv_rs`.

## Harness demonstration

`df3460_harness.c` replicates the lifetime with the exact handler code. Buggy run
(`run.log`, guest DragonFly 6.5-DEVELOPMENT #0):

```
attach          : iv_rs=0x8004902c0 interval=500
read (alive)    : reader gets 500  (ok)
deinit          : iv_rs freed, pointer still 0x8004902c0 (dangling)
window          : chunk reused by poison obj at 0x8004902c0
read (window)   : unprivileged reader gets 0xdeadbeef from FREED chunk
  -> UAF READ confirmed: reader saw the replacement object's bytes (4-byte kernel-heap info leak)
write (window)  : poison.marker2 (offset 8, = interval) now 0x0000012c (was 0xDEADBEEF)
  -> UAF WRITE confirmed: root sysctl write landed in the replacement object
RESULT: BUG REPRODUCED — sysctl handler dereferences freed iv_rs across vap teardown
```

The allocator handed the freed 16-byte chunk straight back to the replacement
object (same address), exactly what DFly's kmalloc size-class zones do
cross-type — so the unprivileged reader observes bytes of an unrelated live
kernel object, and the root writer corrupts one.

Fixed run (`run.fixed.log`): deinit clears `iv_rs`; the handler returns ENXIO
during the window; no freed-chunk read or write.

## Why Low (and the amrr twin that matters more)

Same latency as DF-0730/DF-0731: rssadapt is not selectable in-tree and the guest
has no radio. HOWEVER the identical pattern exists in the **default** algorithm:

* `amrr_sysctl_interval()` (ieee80211_amrr.c:390-403) derefs `vap->iv_rs` freed by
  `amrr_deinit` (ieee80211_amrr.c:135) under the same driver teardown order, and
* `amrr_sysctlattach()` (ieee80211_amrr.c:405-417) registers `amrr_max_sucess_threshold` /
  `amrr_min_sucess_threshold` as `SYSCTL_ADD_UINT` pointing **directly into the
  chunk** — unprivileged reads of those oids in the window UAF-read the freed
  chunk, root writes UAF-write it.

The amrr twin belongs to ieee80211_amrr.c's own audit (DF-3454 there covers a
different defect); it is called out here so it is not lost. On any real
WiFi-equipped DragonFly system the amrr version of this window is reachable
today.

## Fix validation

* `fix.diff`: `rssadapt_deinit` frees only if non-NULL and clears `vap->iv_rs`;
  `rssadapt_sysctl_interval` returns ENXIO when `iv_rs == NULL`.
* `git apply`-clean against sys/; patched `wlan_rssadapt` module rebuilds clean
  under full kernel `-Werror` CFLAGS (`fix_module_build.log`, rc=0).
* Harness `fixed` mode: handler refuses during the window.
* Guest source restored to pristine (sha256 verified) — same one-line fix should
  be applied to ieee80211_amrr.c by its owner.

## Kernel references

sys/netproto/802_11/wlan/ieee80211_rssadapt.c:151-155 (deinit: free without clearing)
sys/netproto/802_11/wlan/ieee80211_rssadapt.c:340-353 (handler derefs vap->iv_rs)
sys/netproto/802_11/wlan/ieee80211_rssadapt.c:356-362 (oid registration, CTLFLAG_RW)
sys/dev/netif/iwm/if_iwm.c:6486-6493 (deinit before vap_detach)
sys/netproto/802_11/wlan/ieee80211.c:752,826-827 (sysctl ctx torn down last)
sys/netproto/802_11/wlan/ieee80211_amrr.c:135,390-417 (reachable amrr twin)

## 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.
