rt2560_scan_end and rt2560_enable_tsf_sync dereference the vap without a NULL check β panic racing last-vap destruction during the scanner's unlocked ic_scan_end call (DF-3968 family)
Summary
rt2560_scan_end dereferences ic->ic_scan->ss_vap->iv_bssid and rt2560_enable_tsf_sync dereferences TAILQ_FIRST(&ic->ic_vaps)->iv_bss->ni_intval with no NULL guard. The scanner calls ic_scan_end with the COM lock dropped (self-annotated 'scan state can change!') while scan_vdetach clears ss_vap and empties ic_vaps under the lock. Destroying the only vap inside that window NULL-derefs -> Fatal trap 12. Identical to sibling DF-3968 (rt2661); rt2560.c even guards the same TAILQ_FIRST correctly elsewhere. Privileged-triggered race (ifconfig destroy vs completing bg scan). NULL-guard fix (module rebuilt -Werror clean).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3969 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.5 KB | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| trigger.c | β | 2.1 KB | view raw | |
| build.sh | β | 67 B | view raw | |
| run.sh | β | 91 B | view raw | |
| run.log | β | 317 B | view raw | |
| env.txt | β | 317 B | view raw | |
| fix.diff | β | 553 B | view raw | |
| build.log | β | 37.9 KB | view raw | |
| manifest.json | β | 698 B | view raw | |
| verdict.json | β | 3.3 KB | view raw |
DF-3969 β rt2560_scan_end / rt2560_enable_tsf_sync dereference the vap unchecked β NULL-deref panic racing last-vap destroy (DF-3968 family)
File: sys/dev/netif/ral/rt2560.c (pass 2, GLM 5.3)
What
rt2560_scan_end (2566β2574) dereferences ic->ic_scan->ss_vap->iv_bss and
rt2560_enable_tsf_sync (2274β2284) dereferences TAILQ_FIRST(&ic->ic_vaps)->iv_bss
with no NULL check. The scanner invokes ic->ic_scan_end unlocked
(ieee80211_scan_sw.c:796β798, self-annotated "scan state can change!"), while
ieee80211_vap_delete β ieee80211_scan_vdetach (ieee80211_scan.c:147β155)
clears ss_vap and empties ic_vaps under the COM lock. Destroying the only
vap inside that window β NULL deref β Fatal trap 12 (local DoS; destroy is
privileged). Identical pattern to DF-3968 on the rt2661 sibling; the rt2560
variant faults on whichever of the two derefs the race leaves stale.
Build / Run
Hardware-gated (needs an RT2560 PCI NIC; this guest has none β env.txt):
cc trigger.c (informational)
# on target: loop ifconfig wlan0 destroy against an active background scan
Expected
Fatal trap 12: page fault while in kernel mode in rt2560_enable_tsf_sync
(read of vap->iv_bss with vap==NULL) or rt2560_scan_end (ss_vap==NULL).
Fix validation
fix.diff adds NULL guards at both sites; git apply --check clean against
sys/, and applied to the guest's /usr/src copy where the full if_ral.ko
module rebuilt clean with -Werror (build.log).
DF-3969 verdict β certain NULL-check gap, race-gated DoS (rt2560 sibling of DF-3968)
Reproduced?
No Ralink NIC on the audit guest (env.txt: pciconf shows no vendor 0x1814 device), so the destroy-vs-scan race could not be exercised. Source-level certainty is high and the pattern is the proven DF-3968 family applied to the rt2560 driver:
- rt2560.c:2566-2574:
vap = ic->ic_scan->ss_vap; ... rt2560_set_bssid(sc, vap->iv_bss->ni_bssid);β no NULL check;rt2560_enable_tsf_sync(sc)is called first and derefs its own unchecked vap first. - rt2560.c:2277,2284:
vap = TAILQ_FIRST(&ic->ic_vaps); ... tmp = 16 * vap->iv_bss->ni_intval;β no NULL check. - Call site: ieee80211_scan_sw.c:796-798 β
scan_end()drops the COM lock aroundic->ic_scan_end(ic)and re-acquires after, explicitly warning that scan state can change during the driver call. - Tear side: ieee80211_scan.c:147-155 β
ieee80211_scan_vdetach(vap)(under IEEE80211_LOCK, from ieee80211_vap_delete) setsss->ss_vap = NULLand the vap leaves ic_vaps; nothing re-validates either the driver's TAILQ_FIRST snapshot or its ss_vap copy. - rt2560.c:2691 (
init_locked) shows the codebase already knows the pattern needs a guard:rt2560_set_macaddr(sc, vap ? vap->iv_myaddr : ic->ic_macaddr)β the two functions fixed here simply lack it.
β ifconfig wlanX destroy racing a completing background scan on the last
vap β NULL deref β panic (local DoS; destroy is privileged). Not exploitable
beyond DoS: both faults are pure reads at small fixed offsets of NULL.
Fix
fix.diff adds NULL guards in rt2560_scan_end (early return when ss_vap is
NULL) and rt2560_enable_tsf_sync (early return when TAILQ_FIRST is NULL) β
two-line defense, same shape as the accepted DF-3968 fix. Validated:
git apply --check clean on sys/; applied on the guest /usr/src copy and
the if_ral.ko module rebuilt clean with -Werror (build.log). Runtime
validation needs the NIC.
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_testableTwo two-line NULL guards eliminate the deref; git apply --check clean against sys/ and the patched module rebuilt -Werror clean (build.log; combined with the other two rt2560 fix.diffs of this batch). Runtime validation impossible without the NIC.
fix.diff; build.log (if_ral.ko linked from patched rt2560.o with -Werror on the guest /usr/src copy)
Confirmed kernel references
Detail
Exploit chain
assoc + background scan running -> concurrent 'ifconfig wlan0 destroy' wins the unlocked ic_scan_end window -> ieee80211_vap_delete/scan_vdetach clears ss_vap and empties ic_vaps -> rt2560_enable_tsf_sync reads vap->iv_bss at NULL (2284) or rt2560_scan_end reads ss_vap->iv_bss (2573) -> kernel panic (DoS).
Evidence (decisive lines)
VERDICT.md (race window path:line incl. net80211 call site and the guarded sibling line rt2560.c:2691), trigger.c (on-target loop), env.txt (no Ralink NIC), fix.diff (NULL guards at both sites), build.log (if_ral.ko rebuilt -Werror clean on the guest /usr/src copy)
PoC changes
Trigger is a documented race recipe (needs NIC + association); informational C stub compiled only.
Verified recommended fix
NULL-guard ss_vap in rt2560_scan_end and TAILQ_FIRST in rt2560_enable_tsf_sync (fix.diff).
Verdict
Certain missing NULL checks: rt2560_scan_end (rt2560.c:2566-2574) dereferences ic->ic_scan->ss_vap->iv_bss and rt2560_enable_tsf_sync (rt2560.c:2277,2284) dereferences TAILQ_FIRST(&ic->ic_vaps)->iv_bss without checking for NULL; the scanner invokes ic_scan_end unlocked (ieee80211_scan_sw.c:796-798, self-annotated 'scan state can change!') and ieee80211_scan_vdetach (ieee80211_scan.c:147-155) clears ss_vap and empties ic_vaps under the lock β destroying the only vap in that window yields NULL -> Fatal trap 12 (privileged-triggered race, local DoS). Identical pattern to the proven sibling finding DF-3968 (rt2661); rt2560 even guards the same TAILQ_FIRST elsewhere (rt2560.c:2691). Not reproduced: the audit guest has no Ralink NIC (env.txt); the on-target trigger recipe (destroy-vs-bgscan loop) is documented in trigger.c.
No comments yet.