β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3438

Cipher registry has no reference accounting: ieee80211_crypto_unregister clears ciphers[] while keys may still hold wk_cipher into the module; policy-module nrefs gate read unsynchronized at MOD_UNLOAD ('XXX disallow if references') - kldunload TOCTOU -> UAF

Summary

ieee80211_crypto_unregister NULLs ciphers[cip] with the code's own admission 'XXX disallow if references'; the policy-module unload gate checks the cipher module's nrefs without synchronization against the nrefs++ the cipher performs in ic_attach under the key-update lock. kldunload wlan_ccmp racing a CCMP key install unlinks the module while key->wk_cipher still points into it; every later ic_encap/ic_decap/ic_detach through that key calls unmapped text. Root-gated both sides, narrow window - hardening-grade (Info). Not runnable on this guest (no radio). DF-0640 still unfixed (noted). Fix: per-cipher reference counter checked under the wlan serialization at unregister/unload.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3438 Β· 3 files
FileTypeDescriptionSize
README.md β€” 1.9 KB ↓ raw
verdict.json β€” 1.9 KB view raw
manifest.json β€” 575 B view raw

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.

Fix verification

not_testable
per-fix-DF-3438

Confirmed kernel references

Detail

Evidence (decisive lines)

['README.md: path:line walk (unregister vs attach/nrefs)']

Verified recommended fix

Add reference counting to the cipher registry so unregister/unload is refused while keys are bound to the cipher.

Verdict

Info/hardening, not exercised (guest has no 802.11 radio, so cipher keys cannot be installed against a real cipher module). Code walk: ieee80211_crypto_unregister (ieee80211_crypto.c:213-228) clears ciphers[cip] with the in-code admission 'XXX disallow if references' - no reference accounting exists; the policy-module unload gate (_IEEE80211_POLICY_MODULE, ieee80211_dragonfly.h:250-262) checks the cipher module's nrefs unsynchronized against the nrefs++ the cipher performs in ic_attach under the key-update lock (ieee80211_crypto_ccmp.c:115). kldunload wlan_ccmp racing a CCMP key install can unlink the module while key->wk_cipher still points into it; subsequent ic_encap/ic_decap/ic_detach calls through that key jump into unmapped module text (UAF). Both trigger sides are root-gated; the window is narrow; severity Info for triage but the consequence is a kernel UAF.