Beacon mbuf write-after-free across disjoint lock domains: rum_update_beacon's unlocked ieee80211_beacon_update window vs m_freem in rum_newstate/rum_vap_delete
Summary
update_beacon drops RUM_LOCK then beacon_update writes into bcn_mbuf's 2KB cluster holding only the ic lock, while newstate frees/replaces under RUM_LOCK (ic released) and vap_delete frees with NO lock - the ic and sc lock sets never overlap on bcn_mbuf. Guest 3-thread lock-domain model: freed-in-window UAF writes 1-13 per 10s across 6/6 runs (userspace floor; the in-kernel TIM/element recompute window is microseconds and preemptible). Fourth member of the beacon family (DF-5246/5254/5259). Hold-lock-across-write + lock-the-vap_delete-free fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5292 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| rum_beacon_race.c | β | 5.7 KB | view raw | |
| build.sh | β | 113 B | view raw | |
| run.sh | β | 108 B | view raw | |
| build.log | β | 16 B | view raw | |
| run.log | β | 382 B | view raw | |
| env.txt | β | 241 B | view raw | |
| VERDICT.md | β | 3.4 KB | β raw | |
| README.md | β | 2.1 KB | β raw | |
| fix.diff | β | 753 B | view raw | |
| verdict.json | β | 4.0 KB | view raw |
DF-5292 β rum beacon mbuf write-after-free across disjoint lock domains
What
rum_update_beacon() (sys/bus/u4b/wlan/if_rum.c:2792-2831) drops RUM_LOCK
at :2825 and then calls ieee80211_beacon_update(ni, m, mcast) at :2828,
which WRITES seqno/capinfo/TIM/ERP element data into rvp->bcn_mbuf's 2 KB
cluster while holding only the net80211 ic lock
(net80211/wlan/ieee80211_output.c:3339 ff.). Two other paths free or
replace that same cluster under disjoint lock domains:
rum_newstate()β releasesIEEE80211_LOCK(ic)at :932, takesRUM_LOCK(sc)at :933, and on S_RUNrum_alloc_beacon()(:977 β :2775-2778) doesm_freem(rvp->bcn_mbuf)+ replace, holding only the sc lock.rum_vap_delete()βm_freem(rvp->bcn_mbuf)at :715 with NO lock at all.
ic-lock vs sc-lock are disjoint β a free landing between :2825 and the end of the in-mbuf writes is a kernel write-after-free on a freed 2 KB cluster.
Twin of DF-5246 (if_run), DF-5254 (if_urtwn), DF-5259 (if_mtw).
Hardware gating
All rum(4) paths need an RT2501/RT2601 USB dongle to attach; the QEMU guest
has none, so in-kernel reproduction is not possible. Evidence is (a) the
line-level lock-domain trace above and (b) a userland interleaving model of
the exact code regions and locks (rum_beacon_race.c), run 3Γ10 s on the
guest. The struct/module if_rum.ko was kldload'ed on the guest to confirm
the audited code is the running build (md5-identical source tree).
Build / Run / Expected
cc -O2 -pthread -Wall -o rum_beacon_race rum_beacon_race.c ./rum_beacon_race 10 # 3 runs
Expected: freed-in-window UAF WRITES > 0 in every run (reachability of the
interleave); see run.log (1, 9, 3 hits in the first series; 5, 2, 13 in the
preemption-mode series β the userspace floor; the in-kernel window is
microseconds of TIM/element recompute and is unbounded under preemption).
Fix
fix.diff β hold RUM_LOCK across the beacon_update in-mbuf writes (and take
it around the m_freem in rum_vap_delete). git apply --check clean.
Kernel fix validation is hardware-gated (fix_status: not_testable).
DF-5292 VERDICT β beacon mbuf write-after-free (rum twin of DF-5246/5254/5259)
Status: inconclusive (hardware-gated). Bug: real, line-proven.
Reproduction path (in-kernel)
- Attach rum(4) device, create
wlan0in hostap or ibss mode, bring it up (S_RUN allocates the beacon viarum_alloc_beacon). - Drive
iv_update_beacon(remote-assistable: TIM updates on station sleep/wake, ERP changes, CSA; local: ioctl-triggered beacon changes) β this entersrum_update_beaconon a net80211/serializer thread holding only the ic lock after :2825. - Concurrently force the other domain:
ifconfig wlan0 down/ re-run (rum_newstateβ S_RUN βrum_alloc_beaconβm_freem(old bcn)under sc lock, :932-933/:2775-2778) orifconfig wlan0 destroy(rum_vap_deleteβm_freemat :715, no lock). - A free landing between :2825 and the end of
ieee80211_beacon_update's writes corrupts the freed 2 KB cluster (write-after-free).
Why inconclusive on this guest
No USB passthrough, no RT2573 device model β rum(4) never attaches β the
vap/beacon lifecycle cannot be entered. (Confirmed: source md5 on guest ==
audit tree; kldload if_rum.ko works; no hardware probe.)
Simulated evidence
rum_beacon_race.c models the three threads with the exact lock protocol
(ic mutex, sc mutex; deleter lock-free). Results (guest, 3Γ10 s):
1/9/3 UAF-write interleavings (base), 5/2/13 (with a mid-write yield modeling
scheduler preemption) β nonzero in 6/6 runs β the interleaving is reachable;
the userspace write burst is nanoseconds, while the in-kernel
ieee80211_beacon_update performs TIM bitmap recopy, trailer memmove and
element rebuilds (Β΅s, preemptible), so the real window is strictly larger.
Exploit chain (if hardware present)
hostap/ibss vap + power-save client TIM churn (remote-peer triggerable)
racing ifconfig down/destroy β ieee80211_beacon_update writes into a
freed 2 KB cluster β heap corruption β panic; with post-free reallocation
control (2 KB zone), controlled overwrite β potential privesc.
Fix
Hold RUM_LOCK(sc) across the beacon_update write phase and around the
m_freem in rum_vap_delete (fix.diff; git apply --check clean).
Lock-order note: the fix takes sc while ic is held (icβsc), which is the
established direction in this driver (rum_update_promisc, rum_wme_update);
RUM_LOCK is LK_CANRECURSE so the nested rum_set_channel path inside CSA
completion is safe. Not kernel-testable without hardware.
Kernel references
- sys/bus/u4b/wlan/if_rum.c:2800 (m = rvp->bcn_mbuf, ic domain only)
- sys/bus/u4b/wlan/if_rum.c:2803-2825 (sc lock taken/dropped)
- sys/bus/u4b/wlan/if_rum.c:2828 (ieee80211_beacon_update writes into m)
- sys/bus/u4b/wlan/if_rum.c:932-933 (ic lock dropped, sc lock taken)
- sys/bus/u4b/wlan/if_rum.c:977 β 2775-2778 (m_freem+replace under sc)
- sys/bus/u4b/wlan/if_rum.c:715 (m_freem with no lock)
- sys/netproto/802_11/wlan/ieee80211_output.c:3339-3470 (beacon_update writes)
- sys/netproto/802_11/ieee80211_dragonfly.h:598 (IEEE80211_LOCK = per-ic mutex)
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-testing is impossible on this guest (no USB passthrough / RT2573 device model), so neither baseline nor patched kernel behavior is exercisable. Hardware-gated: not_testable.
fix.diff (git-apply clean); VERDICT.md lock-order note (ic->sc is the established direction; RUM_LOCK is LK_CANRECURSE)
Confirmed kernel references
- sys/bus/u4b/wlan/if_rum.c:2800
- sys/bus/u4b/wlan/if_rum.c:2825
- sys/bus/u4b/wlan/if_rum.c:2828
- sys/bus/u4b/wlan/if_rum.c:932
- sys/bus/u4b/wlan/if_rum.c:977
- sys/bus/u4b/wlan/if_rum.c:2775
- sys/bus/u4b/wlan/if_rum.c:715
- sys/netproto/802_11/wlan/ieee80211_output.c:3339
- sys/netproto/802_11/ieee80211_dragonfly.h:598
Detail
Exploit chain
hostap/ibss vap + power-save client TIM churn / ERP/CSA beacon updates (remote-peer assistable) racing ifconfig down/destroy or an S_RUN re-entry -> ieee80211_beacon_update writes seqno/capinfo/TIM bitmap into a freed 2KB mbuf cluster -> heap corruption -> panic; with post-free reallocation control of the 2KB zone -> controlled overwrite -> privesc potential
Evidence (decisive lines)
run.log: 'freed-in-window UAF WRITES=1/9/3' then '5/2/13' across 3+3 runs (nonzero every run); rum_beacon_race.c models :2800/:2803-2825/:2828 vs :932-933/:2775-2776 vs :715 with two mutexes and a lock-free deleter; VERDICT.md lock-intersection table
PoC changes
Harness written from scratch (no seed); pthread mutexes model IEEE80211_LOCK(ic) and RUM_LOCK(sc); the deleter thread is deliberately lock-free exactly like rum_vap_delete:715; an atomic poison flag counts would-be UAF writes without killing the run; a sched_yield variant models preemption
Verified recommended fix
Hold RUM_LOCK across the beacon_update in-mbuf writes in rum_update_beacon() and around the m_freem in rum_vap_delete()
Verdict
Beacon-mbuf write-after-free, rum's instance of the run/urtwn/mtw twin family (DF-5246/DF-5254/DF-5259). rum_update_beacon() (if_rum.c:2792-2831) drops RUM_LOCK at :2825 and ieee80211_beacon_update() at :2828 writes seq/capinfo/TIM/ERP data into rvp->bcn_mbuf's 2KB cluster under the net80211 ic lock only, while rum_newstate() (:932-933 drops the ic lock, takes RUM_LOCK; S_RUN -> rum_alloc_beacon :977 -> m_freem at :2775-2776) and rum_vap_delete() (m_freem at :715 with NO lock) free the same cluster in disjoint lock domains. IEEE80211_LOCK is a per-ic lockmgr mutex (ieee80211_dragonfly.h:598), not a global serializer, so the domains really are disjoint. In-kernel reproduction is hardware-gated (no RT2573 device can attach to the QEMU guest; audited source is md5-identical to the guest tree and if_rum.ko loads). Simulated on the guest with a line-faithful 3-thread lock-domain model: freed-in-window UAF writes 1/9/3 (base) and 5/2/13 (with preemption yield) per 3x10s runs - reachable in 6/6 runs; the userspace write burst is the optimistic floor, the in-kernel TIM/element recompute window is microseconds and unbounded under preemption.
No comments yet.