# DF-4650 — ar2316: flat EEPROM power curve → `VpdTable_I[ii][sizeCurrVpdTable-1/-2]` uint32-wrap wild reads (twin of DF-4622/ar5413)

## Verdict: REPRODUCED (harness, verbatim kernel code, exact-address SIGSEGV) — impact: dos (hardware-gated)

## Root cause

`sys/dev/netif/ath/ath_hal/ar5212/ar2316.c` —
`ar2316getGainBoundariesAndPdadcsForPowers()`:

```c
460:  sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii];   /* uint32_t */
461:  tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii];
...
467:  Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] -
468:                         VpdTable_I[ii][sizeCurrVpdTable-2]);
```

`Pmax_t2`/`Pmin_t2` are halves of the EEPROM `pwr_t4` curve endpoints
(ar2316.c:394-404). If the curve for a used pdGain is **flat** (all
EEPROM `pwr_delta_t2` deltas zero → `pwr_t4[numVpd-1] == pwr_t4[0]`),
then `sizeCurrVpdTable == 0` and `sizeCurrVpdTable-1` / `-2` wrap the
`uint32_t` to 0xFFFFFFFF / 0xFFFFFFFE → reads at
`VpdTable_I[ii] + 0x1FFFFFFFE` bytes (**+8.59 GB wild read**). With
`sizeCurrVpdTable == 1` (single step) the `-2` term wraps. Nothing
guards the subtraction; the neighboring unconditional read
`VpdTable_I[ii][1] - VpdTable_I[ii][0]` (ar2316.c:449) stays in-row and
is not itself OOB.

In the kernel the wild read lands in the direct map (garbage `Vpd_step`
→ garbage extrapolated PDADC values programmed to the radio) or in a KVA
hole → panic.

## Trigger (threat model)

Crafted EEPROM on an AR2316-bearing card: any used pdGain whose
`pwr_delta_t2[kk]` entries are all zero for the channel pair bracketing
the target channel (`ar2413EepromToRawDataset` then yields
`pwr_t4 = {v,v,v,v}`, ah_eeprom_v3.c:715-719).

## How it was verified

Verbatim-extraction harness (`ar2316_harness.c` mode 3): four used
gains with flat curves (pwr_t4[0]=124, step 0). The wild address is left
**unmapped** so the wrap faults; the SIGSEGV handler asserts
`si_addr == &vpdTable_I[0][0] + 0x1FFFFFFFE` exactly.

`run.log` (decisive lines):

```
REPRODUCED: SIGSEGV si_addr=0x200402872, expected wild address 0x200402872
MATCH: wild OOB read proven (ar2316.c uint32 counter underflow)
```

The exact-address match proves the read is the `[sizeCurrVpdTable-1]`
uint32 wrap (element index 0xFFFFFFFF × 2 bytes), not any other access.

## Exploitation ceiling

Hardware-gated (crafted EEPROM). Wild KVA read → panic or garbage radio
programming. No exfiltration channel, no uid0 route.

## Fix validation

`fix.diff` (mirrors the accepted DF-4622 fix): clamp
`sizeCurrVpdTable` to ≥2 before the reads. `fix_run.log` (PATCHED mode
3): completes normally (no SIGSEGV), `minPower=0x003e` — a sane value
derived from the dataset.

## 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.
