# DF-4850 — amrr `ath_rate_getxtxrates()` writes hardware rateCodes into `ath_rc_series.rix` (rate-table *index*) fields

**Status: untested (hardware-gated — no Atheros radio on the QEMU guest; amrr is not
compiled into GENERIC, which uses `ath_rate_sample`).** Static verification only,
performed at line level against `sys/`.

## What the bug is (static, certain)

* `struct ath_rc_series.rix` is documented **"ratetable index, not rate code"**
  (`sys/dev/netif/ath/ath/if_athrate.h:91`).
* amrr's per-node cache fields `amn_tx_rate0..3` hold **hardware rate codes** —
  they are assigned `rt->info[x].rateCode` in `ath_rate_update()`
  (`sys/dev/netif/ath/ath_rate/amrr/amrr.c:247-249, 258-260, 267-269, 275-277`).
* `ath_rate_getxtxrates()` (`amrr.c:127-144`) stores those rate codes into
  `rc[0..3].rix` — **including clobbering the correct index** that the caller had
  just stored at `if_ath_tx.c:1392` (`bfs_rc[0].rix = rix`) before calling
  `ath_rate_getxtxrates()` at `if_ath_tx.c:1397`. The `rix0` argument
  (`amrr.c:129`) is ignored entirely.
* Every consumer that treats `rc[i].rix` as a table index then dereferences the
  **wrong entry**:
  - `ath_tx_rate_fill_rcflags()`: `rt->info[rc[i].rix].rateCode/shortPreamble`
    (`if_ath_tx_ht.c:254, 260`) — runs on every data-frame tx dispatch
    (`if_ath_tx.c:1531, 2298, 3034, 5525, 5553, 5740`)
  - A-MPDU delimiter padding: `rix = bfs_rc[0].rix; rt->info[rix].rateCode`
    (`if_ath_tx_ht.c:457-458`)
  - 11n descriptor series: `rt->info[rc[i].rix].rateCode` (`if_ath_tx_ht.c:579-580`)
  - RTS/CTS + duration paths: `if_ath_tx.c:1064, 1128, 1260, 1282`

## Why it is in-bounds (standalone) but still wrong

amrr only ever selects **legacy** negotiated rates from `ni->ni_rates`
(`amrr.c:245-267`; `IEEE80211_RATE_VAL` = 0x7f strips the basic bit, and HT rows
are keyed `>= 0x80` in `sc_rixmap`, `if_ath.c:6203-6208`), so the stored rate
codes are ≤ `0x1b` (27) < `info[64]` (`ah.h:681`) — an in-bounds read of the
wrong entry, never OOB by itself.

Concrete wrong entries:

* **11n-capable HALs** (`ar5416_11ng_table` rateCount=28, `ar5416_11na_table`
  rateCount=24, `ar5416_phy.c:31/69`): legacy rate codes as indices land on
  **HT MCS rows** — e.g. 11ng: rateCode 0x1b (1 Mbps CCK) → `info[27]` = MCS15
  (rateCode 0x8f); 11na: rateCode 0x0b (6 Mbps) → `info[11]` = MCS3. The
  `IS_HT_RATE()` logic in `ath_tx_rate_fill_rcflags` (`if_ath_tx_ht.c:259-318`)
  then sets `ATH_RC_HT_FLAG`/`ATH_RC_CW40_FLAG`/`ATH_RC_SGI_FLAG`/STBC on frames
  whose rate series was chosen as **legacy** — frames are programmed at MCS
  rates the peer may not support, with 40 MHz/SGI flags the channel may not
  allow.
* **Pre-11n HALs** (e.g. `ar5212_11a_table` rateCount=8): indices 8..27 read the
  zero-filled static tail → `rc[i].ratecode = 0` → MRR series / completion
  `ts_rate` of 0, wrong RTS/CTS durations, wrong A-MPDU padding.

## Amplification of known DF-1931

When `sc_rixmap[]` returns the 0xff not-found sentinel (DF-1931, already filed
against this file), `amn_tx_rate0..2` are **garbage bytes** read OOB from
`rt->info[255]` (`amrr.c:246-267`); those garbage bytes then flow into
`rc[i].rix` here and become a **second OOB read** (`rt->info[≤255]` at
`if_ath_tx_ht.c:254` etc.) ~68-190 entries past the 64-entry table. Same run,
two sinks; DF-1931's fix (bounds-check the sentinel) removes that path; the fix
below removes the confusion independently.

## How it would be exercised (needs hardware)

Unauthenticated WiFi peer within radio range on a kernel built with
`device ath_rate_amrr` (`sys/conf/files:264`; GENERIC selects
`ath_rate_sample` — `sys/config/X86_64_GENERIC:266`): the peer's rate set drives
`ath_rate_update`; every multi-rate-retry data frame then walks the wrong-index
derefs above. No QEMU-runnable trigger exists without an Atheros NIC.

## Fix

`fix.diff` in this directory (never applied to `sys/`): store per-series
rate-table indices in `amrr_node` and return them from
`ath_rate_getxtxrates()`, mirroring sample.c's contract-correct
`sn->rates[i].rix` usage.
