# DF-3438 — wlan cipher registry has no reference accounting: module unload TOCTOU vs key attach (`XXX disallow if references`)

`sys/netproto/802_11/wlan/ieee80211_crypto.c`:

- `ieee80211_crypto_unregister` (:213-228) clears `ciphers[cip]` with the
  in-code admission `/* XXX disallow if references */` — it does not know
  how many keys still hold `key->wk_cipher == cip`.
- The policy-module glue `_IEEE80211_POLICY_MODULE`
  (`ieee80211_dragonfly.h:250-262`) gates MOD_UNLOAD on the module's own
  `nrefs`, but that read is **not synchronized** with the
  `nrefs++` the cipher does in `ic_attach` under the key-update lock
  (e.g. `ieee80211_crypto_ccmp.c:115`): `kldunload wlan_ccmp` can pass the
  `nrefs == 0` check concurrently with a vap installing a CCMP key; the
  module then unlinks while `key->wk_cipher` points into its text/data —
  every subsequent `ic_encap/ic_decap/ic_detach` call through that key is
  a call into unmapped memory (UAF).

Also on the same theme: `ieee80211_crypto_register` (:194-207) will not
replace an existing entry, and `ciphers[]` is written without exclusion
against a concurrent `ieee80211_crypto_available`/`newkey` reader — benign
pointer-slot races on aligned words, but the unregister side is the
dangerous one.

Hardening-grade: trigger requires root (`kldunload wlan_ccmp`) racing a
root-driven key install (wpa_supplicant/`ifconfig wpa`), both sides
privileged; impact is kernel UAF (panic → potentially worse). The fix is
the one the code already asks for: count references per cipher
(`nrefs`-style) in `ieee80211_crypto_register/unregister` under the
existing wlan serialization and refuse unload while keys are bound
(clearing `ciphers[]` only when the count drops to zero).

## Verification status

**untested** — no radio hardware on the guest (vap/key installation
against a real cipher needs an 802.11 parent). Code walk is the evidence.
