Unvalidated legacy-rate lookups: rt2860_rates[255] OOB read (tx) and 1<<255 UB (set_basicrates, peer-fed rateset)
Summary
ieee80211_legacy_rate_lookup returns 0xff for unknown rates; rt2860_tx_raw checks the sentinel but rt2860_tx does not - rt2860_rates[0xff] reads ~4.3KB past the 12-entry table, garbage mcs/ack-durations over the air - and rt2860_set_basicrates shifts 1<<0xff where the rateset is peer-supplied via beacon/assoc IEs (UB; wrong BASIC_RATE MMIO value). Robustness/hardening only. Fix: mirror the tx_raw guard.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3753 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| fix.diff | β | 1016 B | view raw | |
| verdict.json | β | 2.1 KB | view raw | |
| env.txt | β | 275 B | view raw | |
| manifest.json | β | 881 B | view raw |
DF-3753 β rt2860 unvalidated legacy-rate lookups: rt2860_rates[255] OOB read and 1 << 255 UB (hardening)
Two sites consume ieee80211_legacy_rate_lookup() (returns rateCodeToIndex[rate],
i.e. a uint8_t that is 0xff for unknown rates, ieee80211_phy.h:172-177) without
checking the sentinel β while a third site in the very same file does check it
(rt2860_tx_raw, rt2860.c:1834-1838), proving the authors' own awareness:
-
rt2860_txrt2860.c:1576-1579:ridx = lookup(...); mcs = rt2860_rates[ridx].mcs;plusrt2860_rates[ridx].sp_ack_dur/lp_ack_dur/phy(rt2860.c:1591-1627). With ridx = 0xff this readsrt2860_rates[255]β the table has 12 entries (rt2860reg.h:968-991, RT2860_RIDX_MAX = 11) β a ~4.3 KB out-of-bounds const-table read; the garbagemcs/durations only affect the over-air encoding/ACK-duration (no kernel memory corruption). Rate sources (tp->mcastrate/mgmtrate/ucastrate,ni->ni_txrate) are net80211-validated today, so reachability needs a misconfiguration β hence Info. -
rt2860_set_basicratesrt2860.c:2356-2364:mask |= 1 << lookup(...)where the rateset comes fromni->ni_ratesβ in hostap/STA modes directly fed by the peer's association/beacon IEs. An unknown basic rate (e.g. an 11a rate element while on 11g) makes the lookup return 0xff β1 << 255is undefined behavior (on amd64 shifts mod 32 β silently sets bit 31) β wrongRT2860_LEGACY_BASIC_RATEMMIO value. Remotely influenced robustness/UB, no memory unsafety.
Fix (fix.diff): mirror the existing ridx == (uint8_t)-1 guard of rt2860_tx_raw
at both sites. Status: Info hardening, source-certain; not runnable on this guest
(no Ralink NIC β env.txt).
Fix verification
not_testableMirrors the driver's own existing guard; compile-consistent.
fix.diff (git apply --check passes against pristine sys/)
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md (both sites + the contrasting checked site), fix.diff (guards both, git apply --check passes)
PoC changes
Analysis-only.
Verified recommended fix
Add the same ridx == (uint8_t)-1 guard rt2860_tx_raw already has to rt2860_tx and rt2860_set_basicrates.
Verdict
Two unvalidated uses of ieee80211_legacy_rate_lookup (sentinel return 0xff for unknown rates, ieee80211_phy.h:172-177): rt2860_tx (rt2860.c:1576-1579, rt2860_rates[255] ~4.3KB past the 12-entry table at rt2860reg.h:975, garbage mcs/ack-durations over the air) and rt2860_set_basicrates (rt2860.c:2362-2363, 1 << 255 UB -- rateset is peer-supplied via assoc/beacon IEs, affecting only the BASIC_RATE MMIO value). The same file checks the sentinel at rt2860_tx_raw (rt2860.c:1834), so the omission is clearly a gap. No kernel memory corruption on either path; classified Info hardening. Not reproduced: guest has no Ralink NIC (env.txt); reachability of path 1 additionally requires a mis-set fixed rate (net80211 validates its rate knobs today).
No comments yet.