ath_vap_delete second TX drain races live taskqueue workers before kfree(avp) β author-acknowledged use-after-free window on vap teardown
Summary
ath_vap_delete disables interrupts but never blocks or drains the driver taskqueue - unlike ath_reset and ath_key_update_begin which block the same queue for exactly this hazard. ieee80211_vap_detach queues a disassoc frame after the first drain (acknowledged by the in-tree 'XXX Danger Will Robinson' comment); a concurrently running/queued sc_txtask/sc_txqtask can be scheduling that frame sw-to-hw while the second ath_draintxq runs, holding buffers whose completion callbacks and av_mcastq linkage reference the avp kfreed at :1860. Use-after-free of struct ath_vap on SMP under TX load during vap destroy - panic at minimum, slab-reuse-dependent for worse. Entry admin-triggered, timing-dependent; race speculative but the missing synchronization is objectively absent and upstream-acknowledged. Fix: taskqueue_block + drains between second drain and kfree.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3618 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.2 KB | β raw | |
| VERDICT.md | β | 2.1 KB | β raw | |
| env.txt | β | 388 B | view raw | |
| fix.diff | β | 900 B | view raw | |
| manifest.json | β | 1.1 KB | view raw | |
| verdict.json | β | 2.4 KB | view raw |
DF-3618 β ath_vap_delete: racy second TX drain around ieee80211_vap_detach β freed-vap use
The defect (author-acknowledged in-tree)
sys/dev/netif/ath/ath/if_ath.c:1766-1884 (ath_vap_delete):
ieee80211_vap_detach(vap); /* 1793 */
/*
* XXX Danger Will Robinson! Danger!
* Because ieee80211_vap_detach() can queue a frame (the station
* diassociate message?) after we've drained the TXQ and flushed
* the software TXQ, we will end up with a frame queued to a node
* whose vap is about to be freed.
* ... This may be racy - the ath task may be running and the
* packet may be being scheduled between sw->hw txq. Tsk.
*/
ath_draintxq(sc, ATH_RESET_DEFAULT); /* 1812 */
...
kfree(avp, M_80211_VAP); /* 1860 */
Sequence that leaves a use-after-free window:
- 1785: interrupts disabled (
ath_hal_intrset(ah, 0)) β but taskqueue tasks already enqueued/running are NOT stopped: nothing here calls taskqueue_block()/taskqueue_drain() (contrast ath_reset:2889/3021 and ath_key_update_begin:3542 which do block the tq). - 1793:
ieee80211_vap_detach()β per the in-tree comment this queues a frame (disassoc) onto TX machinery after the first drain. - That frame is scheduled swβhw by the still-live
sc_txtask/sc_txqtaskworker (enqueued by earlier interrupts, or by ath_tx_kick from another thread) concurrently with the secondath_draintxqat 1812 β the exact interleave the comment itself calls racy. - 1835:
ath_tx_draintxq(sc, &avp->av_mcastq)drains the vap's software mcast queue β a field INSIDE avp β without any synchronization against the task worker that can, in the same window, be manipulating av_mcastq-queued buffers (ath_tx_processqβ completion callbacks that call back into vap methods). - 1860:
kfree(avp)β any buffer whose completion/callback still references the vap (bf_comp handlers, mcastq linkage, ni->ni_vap) now touches freed memory.
Concurrent writers that are not excluded: ath_intr ISRs already past
ath_hal_intrpend (same argument as DF-3617 β intrset(0) at 1785 does not
wait), and the taskqueue thread executing ath_tx_proc*/ath_txq_sched_tasklet.
ath_transmit itself is serialized by net80211 comlock against vap delete,
but the driver's deferred TX machinery is not.
Preconditions
Atheros NIC, active TX (or vap-detach-queued frames), SMP, admin destroys a vap (ifconfig destroy / cloning teardown). The race is timing-dependent β speculative on triggerability, author-acknowledged in the source comment.
Impact if hit
Use of freed struct ath_vap (mcastq linkage, completion callbacks) β
kernel UAF read/write β panic at minimum; slab-reuse dependent for anything
worse.
Why inconclusive here
Guest has no ath hardware (env.txt) β ath_vap_delete on an ath vap cannot be driven; the race additionally needs SMP timing under TX load.
Fix
fix.diff is a targeted mitigation: block+drain the driver taskqueue tasks between the second drain and the kfree, so no deferred task can still hold vap state when it is freed. (Upstream later reworked this whole path; the minimal drain closes the specific window the comment identifies.)
DF-3618 β VERDICT
Status: inconclusive (author-acknowledged race; UAF-class; needs ath hardware + SMP timing to fire; not executable on this guest)
Reproduced?
No β and it cannot be reproduced here: ath_vap_delete requires an attached ath vap; the guest has no Atheros NIC (env.txt). Beyond hardware, the window needs an in-flight/queued taskqueue worker racing the second ath_draintxq under TX load on SMP.
Why this is filed anyway (pass-2 lifetime analysis)
The defect is acknowledged in the source itself (if_ath.c:1796-1810 "XXX Danger Will Robinson! Danger! ... This may be racy - the ath task may be running and the packet may be being scheduled between sw->hw txq."). The audit-independent facts that make it a real lifetime bug and not just a stale comment:
- Interrupts are disabled at 1785 but the driver never blocks/drains its own taskqueue in this path, unlike ath_reset (2889 + 3021) and ath_key_update_begin (3542) which deliberately do.
- A task already executing ath_tx_processq/ath_txq_sched_tasklet holds buffers whose completion path (bf_comp callbacks, ieee80211_tx_complete β ni->ni_vap) and whose queue linkage (avp->av_mcastq, drained at 1835 without exclusion) reference memory kfreed at 1860.
- ieee80211_vap_detach (1793) provably queues a disassoc frame after the first drain β that frame is precisely what the second drain races.
Class: CWE-412/USE-AFTER-FREE window; exploitability speculative (needs slab grooming during a destroy under load β admin-triggered entry, so privilege bar is low value anyway).
Fix validation
Not possible on this guest (no hardware). fix.diff is the minimal taskqueue block+drain mitigation; the real fix is the upstream-style rework of vap teardown ordering.
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.
Fix verification
not_testablefix.diff (mitigation) applies clean via git apply --check; runtime validation impossible without ath hardware
findings/poc/DF-3618/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md full trace with line cites; in-tree comment if_ath.c:1796-1810 acknowledging the race; env.txt (no ath hw); fix.diff git-apply clean
PoC changes
n/a - no seeded PoC; analysis-only pack
Verified recommended fix
taskqueue_block + taskqueue_drain of sc_txtask/sc_txqtask/sc_rxtask between the second ath_draintxq and kfree(avp), mirroring ath_reset's discipline
Verdict
ath_vap_delete (if_ath.c:1766-1884) disables interrupts at 1785 but never blocks or drains the driver taskqueue, unlike ath_reset (2889/3021) and ath_key_update_begin (3542) which block the same queue for exactly this class of hazard. ieee80211_vap_detach (1793) queues a disassoc frame after the first drain (acknowledged by the in-tree 'XXX Danger Will Robinson' comment at 1796-1810); a concurrently-running/queued sc_txtask/sc_txqtask can be scheduling that frame sw-to-hw while the second ath_draintxq (1812) runs, and can hold buffers whose completion callbacks and mcastq linkage reference the avp that is kfreed at 1860. UAF-class window, speculative triggerability (SMP + TX load + vap destroy timing), admin-triggered entry. Not executable on the audit guest: no Atheros NIC (env.txt), and the race needs live hardware TX. Mitigation diff (taskqueue block+drain before kfree) applies clean.
No comments yet.