# DF-5254 — VERDICT

**Finding:** use-after-free *write* on the urtwn beacon mbuf
(`struct urtwn_vap.bcn_mbuf`) because `urtwn_update_beacon()` releases the
driver mutex around `ieee80211_beacon_update()` while
`urtwn_setup_beacon()` / `urtwn_vap_delete()` free-and-replace the same
mbuf from lock domains that are not ordered against it.

## Reproduced?

**On-guest kernel impact: NOT reproduced — hardware-gated.** The guest has
no Realtek RTL8188CUS/EU/8192CU USB device (`kldstat` shows `if_urtwn.ko`
present at `/boot/kernel/if_urtwn.ko` but not loaded; no USB WLAN NIC in
`usbdevs`). `urtwn_attach()` cannot run, so the kernel race cannot be fired
directly. Per the audit protocol this is recorded as **inconclusive** with
strong simulated evidence.

**Simulated evidence: reproduced 6/6 clean runs (100%).** `race_sim.c`
models the exact interleaving with three pthreads mirroring the three
code paths:

| sim thread | kernel path | locks modelled |
|---|---|---|
| A `thread_update_beacon` | `ieee80211_set_tim` (com held) → `urtwn_update_beacon` if_urtwn.c:2196-2216 | com held; sc taken/dropped exactly at 2206/2214 |
| B `thread_newstate` | IBSS merge → `urtwn_newstate` (com dropped at 2551) → `urtwn_setup_beacon` 2168-2171 | sc held, com dropped |
| C `thread_vap_delete` | `urtwn_vap_delete` if_urtwn.c:840-841 | no locks |

A zone-allocator emulation (free-list + generation counters) marks a
cluster dead at `m_freem()`; any subsequent write is a genuine
write-after-free. Observed on guest (DragonFly 6.5-DEVELOPMENT, gcc 8.3,
6 vCPU, unprivileged user):

```
run.log   5/5 runs exit=3 (UAF write detected; gens 220,184,186,404,403)
run.2.log 1/1  exit=3, killer = thread C (vap_delete variant, "vap_delete frees (C): 1")
```

~200 newstate re-entries and ~5000 update iterations per 5s run are enough
to hit the window every time; on real hardware the B path is paced by
remote IBSS merge beacons and the A path by station power-save TIM updates
(both attacker-controllable timing), so the window (the whole duration of
`ieee80211_beacon_update()` on the old pointer, if_urtwn.c:2212) is
realistic.

## Why it is a bug (source trace)

1. `sys/netproto/802_11/wlan/ieee80211_power.c:310-318` —
   `ieee80211_set_tim()` holds `IEEE80211_LOCK(ic)` (com) and calls
   `vap->iv_update_beacon(vap, IEEE80211_BEACON_TIM)`.
2. `sys/bus/u4b/wlan/if_urtwn.c:2196-2216` — `urtwn_update_beacon()`:
   takes `sc_mtx` (2196), ensures `uvp->bcn_mbuf != NULL` (2197-2205),
   **releases `sc_mtx` (2206)**, then `ieee80211_beacon_update(ni,
   uvp->bcn_mbuf, mcast)` (2212) writes into the mbuf, then re-takes
   `sc_mtx` (2214) for `urtwn_tx_beacon`.
3. `sys/bus/u4b/wlan/if_urtwn.c:2551-2552` — `urtwn_newstate()` drops the
   com lock before taking `sc_mtx`, so its call into
   `urtwn_setup_beacon()` (2161-2171: `m_freem(uvp->bcn_mbuf);
   uvp->bcn_mbuf = m;`) is **not serialized** against step 2 by either
   lock: A holds com (not sc), B holds sc (not com).
4. `sys/bus/u4b/wlan/if_urtwn.c:2667-2676` — setup_beacon runs for both
   HOSTAP **and IBSS** opmodes on every S_RUN entry; IBSS S_RUN re-entry
   is remotely triggerable via `ieee80211_ibss_merge()` →
   `ieee80211_sta_join1()` (netproto/802_11/wlan/ieee80211_node.c:630).
5. `sys/bus/u4b/wlan/if_urtwn.c:840-841` — `urtwn_vap_delete()` frees
   `uvp->bcn_mbuf` with no lock whatsoever (Race variant C).

`ieee80211_beacon_update()` writes TIM bitmap / seq / CSA values into the
mbuf (`netproto/802_11/wlan/ieee80211_output.c:3339+`), i.e. the
use-after-free is a **write** primitive into a freed mbuf cluster
(2 KB), repeated at attacker-paced frequency. The twin of this bug family
was confirmed in the if_run.c pass-2 audit (beacon lifecycle /
disjoint-lock-domain).

## Exploitability sketch (why High)

* Object: mbuf cluster (2 KB) in a global cluster pool — freed clusters are
  recycled aggressively on any network load, so the write lands in a
  *live* adjacent object quickly (cross-object corruption, not a
  write-to-free-page).
* Control: attacker controls *when* (TIM update pacing via PS traffic +
  merge beacons) and *content* partially (TIM bitmap bytes, beacon
  contents from the vap's own beacon template).
* Gate: needs a urtwn USB NIC in hostap/ibss operation; attacker is a
  nearby peer (IBSS: fully unauthenticated).
* Ceiling: kernel memory corruption from an unauthenticated remote peer →
  panic now, potentially more with cluster-pool grooming.

## Fix validation

Not kernel-tested (no hardware on the guest). `fix.diff` keeps
`URTWN_LOCK(sc)` held across `ieee80211_beacon_update()` and locks the
free in `urtwn_vap_delete()`; the simulated harness run with the fix's
locking (sc held across A's write phase) shows 0 detections by
construction, since B/C can no longer free while A writes.

## Bottom line

status: **inconclusive** (hardware-gated kernel PoC), reproduced=false,
impact on-guest=none; the code-level defect is **certain to exist** from
the source trace and the simulated interleaving reproduces the UAF write
in 6/6 runs. Recommended severity: **High** (memcorrupt bucket).

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