Deferred rum_cmdq entry holds a raw struct ieee80211vap *; rum_vap_delete() frees the rum_vap without draining sc->cmdq_task - rum_update_beacon_cb/rum_set_beacon execute on freed memory
Summary
update_beacon queues the RAW vap pointer into the 16-slot ring; vap_delete drains only ratectl_task before kfree(rvp) - cmdq_task never drained. Callback reads rvp->bcn_mbuf/iv_txparms and streams mtod bytes to the device on freed memory. Guest model: shipped 11,599-12,071 post-free callback executions per 10s (~61%); drained (fix) mode exactly 0. Twin of DF-5247/DF-5260. draintask-before-free fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5293 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| rum_cmdq_uaf.c | β | 6.0 KB | view raw | |
| build.sh | β | 107 B | view raw | |
| run.sh | β | 156 B | view raw | |
| build.log | β | 16 B | view raw | |
| run.log | β | 871 B | view raw | |
| env.txt | β | 241 B | view raw | |
| VERDICT.md | β | 3.7 KB | β raw | |
| README.md | β | 2.2 KB | β raw | |
| fix.diff | β | 625 B | view raw | |
| verdict.json | β | 4.2 KB | view raw |
DF-5293 β deferred rum_cmdq entry holds a raw vap pointer past rum_vap_delete()
What
rum_update_beacon() queues a DEFERRED command carrying the RAW
struct ieee80211vap *:
sys/bus/u4b/wlan/if_rum.c:2830
rum_cmd_sleepable(sc, &vap, sizeof(vap), 0, rum_update_beacon_cb);
rum_cmd_sleepable() (:745-771) copies the pointer into cmdq[] and merely
ieee80211_runtask()s β asynchronous. The taskqueue thread later runs
rum_cmdq_cb (:723-743) β rum_update_beacon_cb (:2784-2790) β
rum_set_beacon(sc, data->vap) (:2729), which dereferences
rvp->bcn_mbuf (:2733) and vap->iv_txparms (:2744) and streams
mtod(m)/m_pkthdr.len bytes to the device (:2754-2755).
rum_vap_delete() (:709-721) frees rvp->bcn_mbuf (:715) and
kfree(rvp, M_80211_VAP) (:720) β it drains rvp->ratectl_task (:717) but
never drains sc->cmdq_task nor purges cmdq entries referencing the vap.
A queued rum_update_beacon_cb therefore executes on freed memory: UAF read
of the rum_vap (M_80211_VAP, ~0x2c0 bytes) and wild rum_write_multi
read from the freed mbuf, sized by freed-heap garbage.
Twin of DF-5247 (if_run) / DF-5260 (if_mtw). Note: unlike run/mtw's
DF-5249/5262 twins, rum's cmdq ring itself IS properly locked
(producers take RUM_CMDQ_LOCK at :753-766), and rum_key_set/rum_key_delete
copy the key BY VALUE (:3021/:3038) so the DF-5250/5263 raw-key UAF does not
apply β the vap pointer at :2830 is the one dangling reference.
Hardware gating
rum(4) needs an RT2573 USB dongle; the QEMU guest has none. Evidence is the
line-level lifetime trace plus a userland model of the queue/taskqueue/destroy
protocol (rum_cmdq_uaf.c), 3Γ10 s per mode on the guest.
Build / Run / Expected
cc -O2 -pthread -Wall -o rum_cmdq_uaf rum_cmdq_uaf.c ./rum_cmdq_uaf 10 0 # shipped: no drain -> POST-FREE rum_set_beacon > 0 ./rum_cmdq_uaf 10 1 # fixed: draintask before kfree -> exactly 0
See run.log: shipped 11599/11849/12071 post-free executions of the cb;
fixed mode 0/0/0.
Fix
fix.diff β ieee80211_draintask(ic, &sc->cmdq_task) in rum_vap_delete
before the vap memory is freed (what the sim's mode 1 models).
git apply --check clean. Kernel fix validation hardware-gated
(fix_status: not_testable).
DF-5293 VERDICT β deferred cmdq entry executes rum_set_beacon on a freed rum_vap
Status: inconclusive (hardware-gated). Bug: real, line-proven.
Reproduction path (in-kernel)
- Attach rum(4), create hostap/ibss
wlan0, bring up (S_RUN; beacon exists). - Trigger
iv_update_beacon(TIM churn from a power-save client, ERP change, ioctl):rum_update_beaconqueues&vapintocmdq[]andieee80211_runtask(cmdq_task)(:2830, :768). - Before the taskqueue thread executes
cmdq_task(backlogged queue, or plain scheduling latency), destroy the vap:ifconfig wlan0 destroyβrum_vap_deleteβkfree(rvp)(:720). Nothing drainssc->cmdq_taskand nothing purges the entry. - Taskqueue runs
rum_update_beacon_cbβrum_set_beacon(sc, vap): - :2733m = rvp->bcn_mbufβ read of freedrum_vap(also the mbuf itself was freed at :715) - :2744vap->iv_txparms[...]β read of freedrum_vap- :2754-2755rum_write_multi(..., mtod(m), m->m_pkthdr.len)β wild kernel read with freed-heap-controlled length, streamed to the device. Freedrum_vapslots are immediately reusable (any vap create / other M_80211_VAP allocations), so the callback operates on reallocated memory β garbage pointers β panic-capable.
Why inconclusive on this guest
No RT2573 hardware; rum(4) never attaches; the cmdq/vap lifecycle cannot be entered. Structural evidence only (source md5-identical, module loads).
Simulated evidence
rum_cmdq_uaf.c models the 16-slot ring, producers storing the raw pointer,
a taskqueue worker with dispatch latency, and the destroyer running
rum_vap_delete with (mode 0) and without (mode 1) the missing drain.
Guest results (3Γ10 s each): shipped mode β 11599/11849/12071 post-free
rum_set_beacon executions (~61 % of callbacks); drained mode β 0/0/0.
The destroyer's free-window and the worker latency are modeled timings; the
structural claim (no drain β queued entry can outlive the vap) is exact.
Divergences from the twins (checked, NOT affected)
- cmdq ring locking: rum producers take RUM_CMDQ_LOCK (:753-766) β the torn-publish bug of DF-5249/DF-5262 does NOT apply.
- key deferral:
rum_key_set/rum_key_deletecopysizeof(*k)BY VALUE (:3021/:3038) β the raw-key UAF of DF-5250/DF-5263 does NOT apply. - The vap pointer at :2830 is the single dangling deferred reference.
Exploit chain (if hardware present)
post-free rum_vap reallocation (heap groom of M_80211_VAP zone) β
rum_set_beacon reads attacker-shaped bcn_mbuf β rum_write_multi
streams m_pkthdr.len bytes from an attacker-shaped pointer to the device
(info disclosure over the air in hostap mode) plus wild kernel read β
panic; primarily a UAF-driven DoS/disclosure primitive.
Fix
ieee80211_draintask(ic, &sc->cmdq_task) in rum_vap_delete() before
kfree(rvp) (fix.diff, git apply --check clean). Overlaps the DF-5292
fix in rum_vap_delete (trivial merge). Kernel fix validation is
hardware-gated: not_testable.
Kernel references
- sys/bus/u4b/wlan/if_rum.c:2830 (raw &vap queued)
- sys/bus/u4b/wlan/if_rum.c:745-771 (rum_cmd_sleepable: async runtask)
- sys/bus/u4b/wlan/if_rum.c:723-743 (rum_cmdq_cb drains ring)
- sys/bus/u4b/wlan/if_rum.c:2784-2790 (cb derefs data->vap)
- sys/bus/u4b/wlan/if_rum.c:2729-2755 (rum_set_beacon on vap/bcn_mbuf)
- sys/bus/u4b/wlan/if_rum.c:709-721 (vap_delete: no cmdq drain; kfree :720)
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 authored and verified with 'git apply --check' against the pristine sys/ tree; kernel behavior cannot be exercised without an RT2573 device (no USB passthrough/device model on the guest). The userland model demonstrates the fix protocol reduces post-free callback executions from ~61% to exactly 0. Hardware-gated: not_testable.
fix.diff (git-apply clean); run.log fixed-mode lines '=0/0/0'; VERDICT.md
Confirmed kernel references
Detail
Exploit chain
beacon-update cmd queued (:2830) -> ifconfig destroy before the backlogged taskqueue runs cmdq_task -> kfree(rvp) (:720) -> rum_update_beacon_cb reads freed rum_vap: bcn_mbuf wild pointer + garbage m_pkthdr.len -> rum_write_multi streams len bytes from a freed-heap-shaped kernel address to the device (over-the-air disclosure in hostap mode) and wild-reads kernel memory -> panic-capable UAF primitive; M_80211_VAP slot reallocation gives the attacker shape control over the freed object
Evidence (decisive lines)
run.log: shipped mode 'POST-FREE rum_set_beacon=11599/11849/12071', fixed mode '=0/0/0'; rum_cmdq_uaf.c models the 16-slot ring, raw-pointer producer, dispatch-latency taskqueue and the undrained destroy; VERDICT.md lists the two twin-bugs that were checked and do NOT apply to rum
PoC changes
Harness written from scratch (no seed); pthread-based ring + condvar taskqueue with realistic dispatch latency; poison flag on the vap so post-free cb executions are counted rather than crashing; mode 1 adds the exact draintask-before-kfree the fix proposes
Verified recommended fix
ieee80211_draintask(ic, &sc->cmdq_task) in rum_vap_delete() before kfree(rvp), so queued beacon-update cmds referencing the vap complete first
Verdict
rum's instance of the DF-5247 (if_run) / DF-5260 (if_mtw) cmdq-lifetime twin. rum_update_beacon() queues the RAW vap pointer into the deferred command ring (if_rum.c:2830, rum_cmd_sleepable :745-771 only ieee80211_runtask()s it) and the taskqueue later runs rum_update_beacon_cb (:2784-2790) -> rum_set_beacon(sc, data->vap) (:2729) which reads rvp->bcn_mbuf (:2733), vap->iv_txparms (:2744) and streams mtod(m)/m_pkthdr.len bytes to the device (:2754-2755). rum_vap_delete() (:709-721) kfree()s the rum_vap (:720) after draining ONLY rvp->ratectl_task (:717) - sc->cmdq_task is never drained and the ring is never purged, so a queued callback executes on freed (and immediately reusable, M_80211_VAP-zone) memory. Divergences from the twins verified: rum's cmdq ring producers DO take RUM_CMDQ_LOCK (:753-766), so the DF-5249/5262 torn-publish bug does not apply; rum_key_set/rum_key_delete copy the key BY VALUE (:3021/:3038), so the DF-5250/5263 raw-key UAF does not apply - the vap pointer at :2830 is the single dangling deferred reference. In-kernel reproduction is hardware-gated (no RT2573 on the QEMU guest). Simulated on the guest with a line-faithful ring+taskqueue+destroyer model: shipped code produced 11599/11849/12071 post-free rum_set_beacon executions across 3x10s runs (~61% of all callbacks), while adding the missing draintask before kfree yields exactly 0/0/0.
No comments yet.