onoe ath_rate_getxtxrates() stores hardware rate codes into ath_rc_series.rix (rate-table INDEX) fields and clobbers the correct series-0 index β wrong-entry rt->info[] derefs on every multi-rate-retry frame, heap OOB read on 11n HALs (DF-4850 amrr twin)
Summary
Same type confusion as amrr DF-4850: on_tx_rate0..3 are HW rate codes cached by ath_rate_update but written into index-typed rc[].rix, clobbering the caller's correct index - confirmed sinks at if_ath_tx.c:1073/1133/1298 and the 11n path at if_ath_tx_ht.c:588 where HT codes 0x80-0x8f index info[64] (OOB up to ~80 entries). Fires on every mrr data frame; read-only (wrong durations/protection/rates). Cache-indices fix mirroring the amrr shape.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4881 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.1 KB | β raw | |
| VERDICT.md | β | 2.1 KB | β raw | |
| env.txt | β | 1.1 KB | view raw | |
| fix.diff | β | 3.3 KB | view raw | |
| verdict.json | β | 3.6 KB | view raw |
DF-4881 β onoe ath_rate_getxtxrates() writes hardware rate codes into
ath_rc_series.rix (rate-table INDEX) fields β DF-4850 (amrr) twin
What
sys/dev/netif/ath/ath_rate/onoe/onoe.c:135-152:
rc[0].rix = on->on_tx_rate0; /* on_tx_rateN are HW RATE CODES */
rc[1].rix = on->on_tx_rate1;
rc[2].rix = on->on_tx_rate2;
rc[3].rix = on->on_tx_rate3;
struct ath_rc_series.rix is documented uint8_t rix; /* ratetable index,
not rate code */ (sys/dev/netif/ath/ath/if_athrate.h:90-97). onoe caches
hardware rate codes into on_tx_rate0..3 in ath_rate_update
(onoe.c:221 on->on_tx_rate0 = rt->info[on->on_tx_rix0].rateCode; and
:237/:246/:254 for series 1-3). This is the exact shape filed as DF-4850
for amrr.c:128 β verified here as an independent instance in onoe.
Consequences (code-proven)
- Clobber of the correct series-0 index: the caller sets the true
index just before the call (
if_ath_tx.c:1392 bf->bf_state.bfs_rc[0].rix = rix;fromath_rate_findrate), and getxtxrates immediately overwrites it with the rate code (onoe.c:143). - Every downstream consumer then indexes
rt->info[]with a rate code: -ath_tx_calc_protduration:rt->info[rix].phy(if_ath_tx.c:1073) -ath_tx_calc_duration:rt->info[rix].spAckDuration/.lpAckDuration(if_ath_tx.c:1133-1135) -ath_tx_set_rtscts:rt->info[rix].controlRate(if_ath_tx.c:1298) - 11n path:ath_rateseries_setup:series[i].Rate = rt->info[rc[i].rix].rateCode;(if_ath_tx_ht.c:588) then programs the value intoath_hal_set11nratescenario. - OOB read on 11n HALs: HAL rate codes for HT are 0x80..0x8f
(128-143, e.g. ar5416_11na_table, ar5416_phy.c:80-90) while
HAL_RATE_TABLE.info[]has 64 entries (ah.h:681) βrt->info[143]reads ~80 entries (~1.2 KB) past the allocated table; the garbage.rateCode/.phy/.controlRate/durationsare then programmed into TX descriptors. On pre-11n HALs the codes (0x0b-0x1e) stay under 64, so they are in-bounds but wrong-entry derefs (wrong ACK/RTS durations, wrong protection decisions).
Trigger conditions
if_ath_tx.c:1396-1397: called for every frame with
bfs_ismrr && try0 != ATH_TXMAXTRY β i.e. every multi-rate-retry data
frame on sc_mrretry hardware (onoe sets on_tx_try0 = 4 β ATH_TXMAXTRY,
onoe.c:233). Requires an Atheros NIC running the onoe ratectl module
(non-default; sample ships as default, X86_64_GENERIC:266).
Threat
Attacker: any 802.11 peer (traffic alone triggers it β no crafted input needed; the bug is unconditional once onoe+mrr hardware is in use). Impact: heap OOB read feeding hardware TX programming β wrong durations/rates/mis-programmed 11n scenarios; no direct info leak to userspace. Functional/degradation impact; consistent with DF-4850 (Low).
Why not reproduced on the QEMU guest
No Atheros NIC in the guest (env.txt); getxtxrates is only reached from the ath TX path. Static chain fully cited above.
Fix
Populate rc[].rix with the table indices and put the codes in
rc[].ratecode β see fix.diff (mirrors the amrr fix shape).
DF-4881 VERDICT
Status: inconclusive (guest has no Atheros hardware; the defect is proven statically with the same rigor as its amrr twin DF-4850).
What was verified
- The type confusion is in the source, unconditionally:
on_tx_rate0..3are assignedrt->info[...].rateCodeat onoe.c:221, :237, :246, :254 and stored intorc[0..3].rixat onoe.c:143-146.ath_rc_series.rixis documented "ratetable index, not rate code" (if_athrate.h:91). - The caller's correct
rc[0].rix(set at if_ath_tx.c:1392 fromath_rate_findrate'son_tx_rix0) is clobbered by onoe.c:143. - Downstream index-derefs of
bfs_rc[].rixconfirmed at: if_ath_tx.c:1073 (rt->info[rix].phy), if_ath_tx.c:1133-1135 (spAckDuration/lpAckDuration), if_ath_tx.c:1298 (controlRate), if_ath_tx_ht.c:588 (series[i].Rate = rt->info[rc[i].rix].rateCode). - OOB reachability on 11n HALs: HT rateCode values 0x80-0x8f (128-143;
ar5416_phy.c:80ff) vs
HAL_RATE_TABLE.info[64](ah.h:681) β up to ~80 entries past the table. - Trigger: every mrr frame (
bfs_ismrr && try0 != ATH_TXMAXTRY, if_ath_tx.c:1396-1397); onoe always sets try0=4 on sc_mrretry parts (onoe.c:233).
Guest environment
No Atheros NIC (pciconf: only virtio vgapci, env.txt); onoe module not shipped (ath_rate.ko = sample). The ath TX path cannot be entered.
Impact ceiling
Heap OOB read (bounded, read-only) whose garbage output is programmed into TX descriptors β wrong ACK/RTS durations, wrong protection rate decisions, mis-programmed 11n rate scenarios (protocol misbehavior / link degradation). No confidentiality impact. Low severity, matching DF-4850.
Fix validation
Not possible on this guest. fix.diff authored against the read-only tree. fix_status: not_applicable.
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 (cache per-series indices in onoe.h, populate .rix with indices and .ratecode with codes) but not kernel-buildable-testable here - the ath TX path needs Atheros hardware the guest lacks.
findings/poc/DF-4881/fix.diff
Confirmed kernel references
- sys/dev/netif/ath/ath_rate/onoe/onoe.c:143
- sys/dev/netif/ath/ath_rate/onoe/onoe.c:221
- sys/dev/netif/ath/ath/if_athrate.h:91
- sys/dev/netif/ath/ath/if_ath_tx.c:1392
- sys/dev/netif/ath/ath/if_ath_tx.c:1396
- sys/dev/netif/ath/ath/if_ath_tx.c:1073
- sys/dev/netif/ath/ath/if_ath_tx.c:1133
- sys/dev/netif/ath/ath/if_ath_tx.c:1298
- sys/dev/netif/ath/ath/if_ath_tx_ht.c:588
- sys/dev/netif/ath/ath_hal/ah.h:681
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_phy.c:80
Detail
Exploit chain
802.11 peer exchanges traffic with an ath+onoe+mrr station -> every multi-rate-retry frame takes ath_tx_do_ratelookup -> onoe getxtxrates poisons bfs_rc[0..3].rix with rate codes -> rt->info[rateCode] wrong-entry/OOB reads (if_ath_tx.c:1073/1133/1298; if_ath_tx_ht.c:588) -> garbage durations/protection/rates programmed to hardware. No path to memory write or info leak to userspace; impact is mis-programmed transmission.
Evidence (decisive lines)
README.md (sink-by-sink citations), VERDICT.md, env.txt (no ath hardware). Decisive lines: onoe.c:143-146 (.rix = rate codes), onoe.c:221 (rate0 = rt->info[].rateCode), if_athrate.h:91 (.rix contract), if_ath_tx.c:1392 (clobbered index), if_ath_tx_ht.c:588 (series Rate = rt->info[rix].rateCode), ar5416_phy.c:80-90 (HT codes 0x80-0x8f), ah.h:681 (info[64]).
PoC changes
n/a - no runnable PoC without ath hardware; evidence is the fully-cited static chain
Verified recommended fix
Cache rate-table indices per series in onoe_node and write them to rc[].rix (codes to rc[].ratecode), mirroring the amrr DF-4850 fix
Verdict
Type confusion proven statically (DF-4850/amrr twin): onoe ath_rate_getxtxrates (onoe.c:143-146) stores cached hardware rate codes (on_tx_rate0..3, assigned rt->info[...].rateCode at onoe.c:221/237/246/254) into struct ath_rc_series.rix fields whose contract is 'ratetable index, not rate code' (if_athrate.h:91), clobbering the correct series-0 index the caller installed at if_ath_tx.c:1392. Downstream rt->info[rc[i].rix] derefs confirmed at if_ath_tx.c:1073, 1133-1135, 1298 and if_ath_tx_ht.c:588 (11n rate scenario programming). On 11n HALs HT rate codes are 0x80-0x8f (128-143) vs HAL_RATE_TABLE.info[64] (ah.h:681) => heap OOB read up to ~80 entries past the table; garbage .rateCode/.phy/.durations then program TX descriptors. Triggered by ordinary mrr-frame transmission (if_ath_tx.c:1396-1397, try0=4 != ATH_TXMAXTRY on sc_mrretry parts). Not exercisable on this guest: no Atheros NIC, no onoe module (env.txt). Low severity: read-only OOB feeding HW registers, no confidentiality impact.
No comments yet.