# DF-4141 — ar5211GetLowerUpperValues empty-pier-list OOB stack read (DF-3977 ar5211 twin)

File: `sys/dev/netif/ath/ath_hal/ar5211/ar5211_reset.c`

## What

`ar5211GetLowerUpperValues()` (ar5211_reset.c:1813) loads
`const uint16_t listEndValue = *(pList + listSize - 1);` at :1818 **before
any validation of `listSize`**. When the caller passes `listSize == 0`, this
is a 2-byte read **below** the caller's array (`pList[-1]`), and the
subsequent first-element test at :1829 reads `*pList`, which is
uninitialized whenever the caller did not fill the array.

Kernel reachability (source-certain): `ar5211SetRateTable()`
(ar5211_reset.c:1528-1538) copies a CTL's `RD_EDGES_POWER` row into
`uint16_t tempChannelList[NUM_EDGES]` on the stack, stopping at the first
zero `rdEdge`; a **matched** CTL (`ee_ctl[i] == cfgCtl != 0`,
ar5211_reset.c:1357-1361) whose first edge is blanked
(`rdEdge == 0 && twice_rdEdgePower == 0` — degenerate/corrupt/crafted
EEPROM, loaded by `readEepromCTLInfo()` in ah_eeprom_v3.c:1013) leaves
`numEdges == 0` and `tempChannelList` untouched, then calls
`ar5211GetLowerUpperValues(freq, tempChannelList, 0, ...)` at :1537.

Impact: OOB/uninitialized stack values become `lowerChannel/upperChannel`,
selecting a `twice_rdEdgePower` (`twiceMaxEdgePower`, :1545-1550) that no
longer reflects the regulatory edge limit → TX power programmed above the
conformance-test limit for the channel. No kernel write primitive; this is
the ar5211 copy of the ar5212 defect filed as DF-3977 (the two HALs carry
textually identical helper code).

## Build / Run (guest, no hardware needed — proof of primitive)

    # build
    cc -O2 -Wall -o df4141_oob df4141_oob.c
    # run — SUCCESS = prints the SIGSEGV marker and exits 33
    ./df4141_oob ; echo "exit=$?"     # expect: exit=33

The harness places the array at the start of an mmap'd page whose
predecessor page is PROT_NONE, so the `pList[-1]` load at :1818 faults
deterministically. Runs: 3/3 faults (run.log, run.2.log, run.3.log).

Fix validation (harness-level): `df4141_oob_fixed.c` carries the verbatim
function **with the exact guard from fix.diff** — it returns cleanly with
`lower=0 upper=0` (fix_run.log, exit=1 "NOT-FAULTED" branch by design).
`fix.diff` additionally `git apply --check`s cleanly against the sys/ tree
(read-only audit; never applied).

## Kernel-level manifestation

Hardware-gated: the guest has no AR5211 NIC (0 atheros/prism PCI devices),
so the in-kernel path cannot be triggered there. In-kernel effect would be
a 2-byte stack OOB read + uninitialized stack read inside ar5211Reset()
(the values never leave the HAL; they misprogram radio power).
