Unlocked ss_ops dispatch TOCTOU: ieee80211_scan_timeout/iterate/flush check-then-call ss->ss_ops with no com lock while ieee80211_scan_vdetach's scan_detach kfrees ss->ss_priv (never cleared) - scan_age on freed table
Summary
ieee80211_scan_timeout is invoked from the ieee80211_node_timeout callout which holds NO com lock; scan_iterate and scan_flush dispatch unlocked the same way. The teardown ieee80211_scan_vdetach holds the lock, calls scan_detach -> sta_detach which kfrees ss->ss_priv (the whole sta_table) WITHOUT clearing it, then sets ss_ops=NULL. The check-then-call window lets the timer's scan_age run the table lock + TAILQ walks on freed memory. DF-0587 family (lock-discipline hole at a new site); root-gated trigger (vap destroy) racing the inact timer. Not runnable on this guest (no 802.11 radio; DF has no virtual 802.11 driver) - untested with interleaving diagram in pack. Fix: IEEE80211_LOCK around the unlocked dispatches + clear ss_priv at the top of sta_detach.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3437 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.2 KB | β raw | |
| verdict.json | β | 2.2 KB | view raw | |
| manifest.json | β | 730 B | view raw |
DF-3437 β wlan scan dispatchers run ss_ops->β¦ without the com lock, racing ieee80211_scan_vdetach's scan_detach which frees ss->ss_priv
sys/netproto/802_11/wlan/ieee80211_scan.c dispatches through
ss->ss_ops without IEEE80211_LOCK in:
ieee80211_scan_timeout(:582-588) β called from theieee80211_node_timeoutcallout, which takes no com lock (ieee80211_node.c:2243-2256);ieee80211_scan_iterate(:626-633) andieee80211_scan_flush(:639-647).
The teardown side, ieee80211_scan_vdetach (:137-155), does hold the
lock and calls ss->ss_ops->scan_detach(ss) β sta_detach
(ieee80211_scan_sta.c:186-199) which kfrees ss->ss_priv (the whole
sta_table) and never clears ss->ss_priv; ss->ss_ops = NULL happens
only afterwards in vdetach. Interleaving:
callout: if (ss->ss_ops != NULL) /* unlocked read - true */
vdetach: (lock) scan_detach(ss) -> sta_detach: kfree(ss->ss_priv)
vdetach: ss->ss_ops = NULL; (unlock)
callout: ss->ss_ops->scan_age(ss) /* stale ops; sta_age derefs
the freed ss_priv under
SCAN_TABLE_LOCK(st) on freed
memory */
scan_age (ieee80211_scan_sta.c) immediately does
st = ss->ss_priv; IEEE80211_SCAN_TABLE_LOCK(st); TAILQ_... β lock
acquire and list walk on freed memory. Trigger:
ifconfig wlanX destroy (root) racing the per-com inactivity timer.
UAF β panic/corruption; root-gated trigger, tiny window, but the
pattern is a real TOCTOU on an unlocked function-pointer dispatch
(the DF-0587 family β same lock-discipline hole, different site).
Verification status
untested β the guest has no 802.11 radio device and DF has no virtual
802.11 driver, so a vap cannot be created (ifconfig wlan0 create wlandev
β¦ requires a parent radio). The 802.11 pass-1 findings with the same
constraint (DF-0640, DF-0587) were left unverified for the same reason.
Code walk above is the evidence; fix is to take IEEE80211_LOCK(ic)
around the unlocked dispatchers (or make sta_detach clear ss->ss_priv
and the readers re-validate under the lock).
Fix verification
not_testableConfirmed kernel references
- sys/netproto/802_11/wlan/ieee80211_scan.c:582
- sys/netproto/802_11/wlan/ieee80211_scan.c:586
- sys/netproto/802_11/wlan/ieee80211_scan.c:626
- sys/netproto/802_11/wlan/ieee80211_scan.c:639
- sys/netproto/802_11/wlan/ieee80211_scan.c:147
- sys/netproto/802_11/wlan/ieee80211_node.c:2253
- sys/netproto/802_11/wlan/ieee80211_scan_sta.c:186
Detail
Evidence (decisive lines)
['README.md: interleaving diagram + path:line walk', 'ieee80211_node.c:2253 (unlocked callout call), ieee80211_scan.c:586-587 (unlocked ss_ops check-then-call), ieee80211_scan_sta.c:186-199 (sta_detach frees ss_priv, never clears it)']
Verified recommended fix
Take IEEE80211_LOCK(ic) around the ss_ops dispatches in ieee80211_scan.c and clear ss->ss_priv in sta_detach().
Verdict
Not exercised on the guest: the QEMU target has no 802.11 radio and DragonFly has no virtual 802.11 driver, so no vap can be created to drive the scan framework (same constraint that left DF-0640/DF-0587 unverified in pass 1). The defect is established by code walk: ieee80211_scan_timeout (ieee80211_scan.c:582-588, called from the unlocked ieee80211_node_timeout callout, ieee80211_node.c:2243-2256) and scan_iterate/scan_flush (:626-647) dispatch through ss->ss_ops with no com lock, while ieee80211_scan_vdetach (:137-155) under the lock calls scan_detach -> sta_detach (ieee80211_scan_sta.c:186-199) which kfrees ss->ss_priv (the sta_table) without clearing it before ss->ss_ops is NULLed. The check-then-call window lets the timer's scan_age run SCAN_TABLE_LOCK + TAILQ walks on freed memory. Root-gated trigger (ifconfig wlanX destroy) racing a once-per-interval callout; DF-0587 family (lock-discipline hole at a different site).
No comments yet.