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

Beacon mbuf use-after-free write: mtw_update_beacon manipulates rvp->beacon_mbuf with no MTW_LOCK (the port dropped FreeBSD's RUN_LOCK), racing m_freem in mtw_newstate(S_RUN)/mtw_vap_delete under the disjoint sc-lock domain

Summary

update_beacon touches beacon_mbuf under only the ic lock while newstate/vap_delete free it under only MTW_LOCK - the port literally dropped FreeBSD's RUN_LOCK wrapper. TIM churn vs down/destroy or RUN transition = UAF write into a freed 2KB mbuf cluster. Guest sim (exact code regions, both lock domains): 127,355/127,131/127,406 write-after-free interleavings per 10s (~91% of frees land in the unlocked window); hardware-gated -> inconclusive. Third member of the beacon family (DF-5246/DF-5254). MTW_LOCK wrap fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5259 Β· 5 files
FileTypeDescriptionSize
beacon_race.c β€” 3.6 KB view raw
env.txt β€” 183 B view raw
fix.diff β€” 566 B view raw
run.log β€” 0 B ↓ download
verdict.json β€” 3.2 KB view raw

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored and verified with 'git apply --check' against the pristine sys/ tree, but cannot be kernel-tested: the guest has no USB controller and no MT7601U device model, so neither baseline nor patched kernel behavior is exercisable. Hardware-gated: not_testable.

fix.diff (git-apply clean); VERDICT.md
↓ fix.diffper-fix-DF-5259

Confirmed kernel references

Detail

Exploit chain

hostap/ibss vap + power-save client TIM churn (remote-peer triggerable) racing ifconfig down/destroy or a RUN transition -> ieee80211_beacon_update writes into freed 2KB cluster -> heap corruption -> panic to privesc with post-free reallocation control

Evidence (decisive lines)

run.log: 'UAF writes: 127355/127131/127406' over 3 x 10s runs; beacon_race.c; VERDICT.md lock-intersection table

PoC changes

Harness written from scratch (no seed); pthread mutexes model IEEE80211_LOCK(ic) and MTW_LOCK(sc); atomic poison flag so the process survives while counting kernel-equivalent UAF writes

Verified recommended fix

Take MTW_LOCK around the beacon_mbuf check/alloc/use in mtw_update_beacon() (what FreeBSD run_update_beacon does)

Verdict

Same lock-domain hole as the run/urtwn twins (DF-5246/DF-5254), but here the lock was outright DROPPED in the port: mtw_update_beacon() (if_mtw.c:3829-3868) touches rvp->beacon_mbuf (NULL-check, ieee80211_beacon_alloc, ieee80211_beacon_update which WRITES seq/capinfo/TIM into the mbuf) under only the net80211 ic lock, while mtw_newstate() at 1802-1803 (after releasing the ic lock at 1771) and mtw_vap_delete() at 891 m_freem() the same mbuf under only MTW_LOCK - the two lock sets are disjoint, so beacon_update can write into a freed 2KB mbuf cluster. FreeBSD's run_update_beacon() wraps exactly this region in RUN_LOCK. Simulated on the guest with a harness mirroring the exact code regions and lock domains: 127131-127406 write-after-free interleavings per 10s run (~91% of frees land inside the beacon_update window) across 3 runs. In-kernel reproduction is hardware-gated (no MT7601U can attach to the QEMU guest).