# DF-3977 — ar5212GetLowerUpperValues with an empty pier list: uninitialized stack reads and a 2-byte OOB stack read (ep[-1]) below tempChannelList

- File: `sys/dev/netif/ath/ath_hal/ar5212/ar5212_reset.c:2626-2669` (sink)
  callers: `ar5212GetTargetPowers` (2568-2583) and `ar5212GetMaxEdgePower` (2492-2508)
- Class: CWE-125 / CWE-457 (Low) — checklist items (a)/(c) pass-2 verification for this HAL.

## Build / Run / Expected

Hardware-gated: requires an AR5212/AR5213/AR5413-family card whose EEPROM has:

- a target-power group with every testChannel == 0 (→ `ee_numTargetPwr_11a/11b/11g == 0`;
  the v3 parser computes counts by counting non-zero testChannels — ah_eeprom_v3.c:964-1002), or
- a CTL with a non-zero index byte but an all-zero edge block (→ `numEdges == 0` in
  `ar5212GetMaxEdgePower` — the rdEdge==0 break fires at i=0, ar5212_reset.c:2500-2505).

Both are plain zeroed/corrupted-EEPROM states — no precise crafting needed.

## Root cause

```c
void ar5212GetLowerUpperValues(uint16_t v, uint16_t *lp, uint16_t listSize, ...)
{
	uint32_t target = v * EEP_SCALE;
	uint16_t *ep = lp+listSize;                 /* == lp when listSize==0 */
	...
	if (target < (uint32_t)(lp[0] * EEP_SCALE - EEP_DELTA)) {   /* UNINIT read: tempChannelList never filled */
	if (target > (uint32_t)(ep[-1] * EEP_SCALE + EEP_DELTA)) {  /* OOB read 2 bytes BELOW the array */
```

With `listSize == 0` the caller's `tempChannelList[]` was never written (both callers fill it in a
loop bounded by the count), so `lp[0]` is uninitialized stack and `ep[-1]` reads one uint16 below
the array. The returned `*vlo/*vhi` (garbage or 0) then drive index selection:

- `ar5212GetMaxEdgePower:2510-2513`: the "find lower index" loop never runs (`numEdges==0`), the
  `HALASSERT(i != numEdges)` is compiled out in production builds, and `pRdEdgesPower[0].flag /
  twice_rdEdgePower` of a CTL that was declared empty decide the edge power — worst case
  `MAX_RATE_POWER` (63) is programmed for the channel (regulatory-limit bypass on the radio), or a
  garbage-derived value.
- `ar5212GetTargetPowers:2586-2608`: ixlo/ixhi default to 0 → the zeroed (M_ZERO-allocated) EEPROM
  target powers are used → TX power collapses to 0.

Memory-safety exposure is confined to two stack reads (one uninitialized, one 2 bytes below a
16-byte local array); no disclosure to userspace and no write primitive — hence Low, AV:P,
consistent with DF-1308/1309/3960.

## Verified NOT vulnerable (pass-2 negative results for the checklist)

- (a) ctlEdges/regdomain walks: `ee_numCtls` is never raw EEPROM data — the v3 parser hardcodes
  {16,32} (ah_eeprom_v3.c:1147-1152) against `ee_ctl[32]`/`ee_rdEdgesPower[8*32]`
  (ah_eeprom_v3.h:416,445); the v14/v4k/9287 backends compute `ee_numCtls = i` bounded by
  AR5416(4K/9287)_NUM_CTLS with a sizeof-fit assert (ah_eeprom_v14.c:283-305 etc.).
- target-power counts are computed (11a≤8, 11g≤3, 11b≤2 — ah_eeprom_v3.c:939/947/955) and
  `tempChannelList[NUM_TEST_FREQUENCIES=8]` can never overflow.
- (b) GetGainBoundariesAndPdadcs does not exist in this file (RF backends ar2413/ar5413/ar2316/
  ar2317/ar2425 own it) — out of scope here.
- (d) spur mitigation (1750-1922): all mask shifts provably ≤ 31; binWidth assigned in every branch;
  callers guarantee OFDM (non-B) channels.
- ar5212CorrectGainDelta (2416-2486): `cckIndex` and `ratesIndex[]` clamps hold for every
  EEPROM-reachable value of ee_cckOfdmPwrDelta (0..255) and ee_cckOfdmGainDelta (0..255) — proved
  by range analysis; `ah_pcdacTable` writes stay in [0,63].
- the `lp[1]` past-the-end read in the walk loop is unreachable for both callers because
  target = freq*100 ≡ 0 (mod 100) can never sit in the (last*100-10, last*100+10) non-return band —
  the final iteration always returns at the "close" check.
- FreeBSD/OpenBSD CVE family for this HAL (beacon/DMA/timer bugs) does not intersect this file's
  code paths; the EEPROM OOB family (DF-1308/1309/3960-era fixes) is bounded here as shown above.
