# DF-5254 — urtwn beacon mbuf use-after-free (disjoint lock domains)

## What
`urtwn_update_beacon()` manipulates `uvp->bcn_mbuf` with the driver mutex
(`sc_mtx`) **released** while it calls `ieee80211_beacon_update()`
(sys/bus/u4b/wlan/if_urtwn.c:2206-2212). Two other paths free/replace that
same mbuf under **different** (or no) lock domains:

* `urtwn_setup_beacon()` — `m_freem(uvp->bcn_mbuf); uvp->bcn_mbuf = m;`
  (if_urtwn.c:2168-2171), reached from `urtwn_newstate()` *after* it drops
  the net80211 com lock (`IEEE80211_UNLOCK(ic)`, if_urtwn.c:2551) and takes
  `sc_mtx`. Runnable S_RUN re-entry is attacker-reachable in IBSS mode:
  a crafted beacon with a larger TSF triggers `ieee80211_ibss_merge()` →
  `ieee80211_sta_join1()` → full state-machine re-entry → RUN
  (netproto/802_11/wlan/ieee80211_node.c:610-630).
* `urtwn_vap_delete()` — `m_freem(uvp->bcn_mbuf)` with **no lock at all**
  (if_urtwn.c:840-841).

Meanwhile `iv_update_beacon` is invoked with the **com** lock held
(e.g. `ieee80211_set_tim()`, netproto/802_11/wlan/ieee80211_power.c:310-318,
driven by station power-save traffic — i.e. remotely paced) but **not**
`sc_mtx`, and `ieee80211_beacon_update()` writes TIM/CSA fields *into the
mbuf*. No lock orders the writer (A) against the freer (B/C):
A holds com, B holds sc, C holds nothing → classic use-after-free **write**
on an mbuf cluster (m_freem returns the cluster to the mbuf cluster pool
immediately).

## Reproduce
Hardware-gated on-guest (no RTL8188CUS/EU USB device attached to the QEMU
guest, `kldstat` shows urtwn not loaded), so this pack contains a faithful
**two-lock-domain race simulation** of the exact interleaving instead:

```
./build.sh          # cc -O2 -pthread -Wall -o race_sim race_sim.c
./run.sh            # 3-5 runs x 5s; exit code 3 == UAF write detected
```

Expected (observed 6/6 clean runs on the guest, unprivileged user):

```
[UAF-WRITE] thread A wrote into a freed beacon mbuf (gen NNN) --
   freed by concurrent setup_beacon/vap_delete while A was inside
   ieee80211_beacon_update() with sc_mtx released (if_urtwn.c:2206-2212)
RESULT: USE-AFTER-FREE WRITE reproduced
exit=3
```

`run.2.log` shows the `vap_delete` variant firing (C frees = 1).

## Fix
See `fix.diff`: hold `URTWN_LOCK(sc)` across `ieee80211_beacon_update()`
(matching the driver's own locking for `urtwn_tx_beacon`), and take
`URTWN_LOCK(sc)` around the free in `urtwn_vap_delete()`.

## Files
* `race_sim.c` — pthread model of the interleaving (zone-allocator
  emulation, generation counters; stale-generation write == write-after-free)
* `build.sh`, `run.sh`, `build.log`, `run.log`, `run.2.log`, `env.txt`
* `fix.diff`, `manifest.json`, `verdict.json`
