# DF-4624 — ar9285 numXpdGain==0 → pdadcValues[-1] OOB read + uninit stack read

## What

`ar9285GetGainBoundariesAndPdadcs()` (sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:636-801)
executed with `numXpdGains == 0` — which a v4k EEPROM with
`modalHeader.xpdGain == 0` produces, because the xpd-gain mask loop at
ar9285_reset.c:593-602 never increments `numXpdGain` (the `HALASSERT`+`break`
at :595-598 only guards > 2 gains, never 0 gains) —

* reads **uninitialized stack** `minPwrT4[0]` at ar9285_reset.c:719
  (`*pMinCalPower = (int16_t)(minPwrT4[0] / 2)` — GCC itself warns on the
  verbatim extraction), and
* runs the PDADC tail backfill at ar9285_reset.c:796-798 with `k == 0`:
  `pPDADCValues[k] = pPDADCValues[k-1];` evaluates **`pdadcValues[-1]`** —
  a 1-byte OOB read below the caller's
  `static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]`
  (ar9285_reset.c:570) — and replicates that byte into all 128 PDADC
  values, which `ar5416WritePdadcValues()` (ar9285_reset.c:628) programs
  into the PHY power-detector ADC table.

This is the AR9285/v4k sibling of the AR5413 finding DF-4620 (same family,
same trigger shape `xpd_mask == 0`, smaller blast radius: the ar9285
counters are `int`, so the index is `-1`, not a wrapped `0xFFFFFFFF`).

## Threat model

Same as the already-filed DF-1768/DF-1769 for this file: the v4k EEPROM
contents (including `xpdGain`, uint8_t, ah_eeprom_v4k.h:93) come off the
card/dongle verbatim after a non-cryptographic checksum; a malicious or
corrupt AR9285/AR9271 dongle EEPROM with `xpdGain == 0` reaches this on the
first channel set (`ifconfig ath0 up` → ar9285SetTransmitPower →
ar9285SetPowerCalTable → ar9285GetGainBoundariesAndPdadcs). No in-kernel
validation rejects `xpdGain == 0`.

## Build / run (DragonFly guest)

    sh build.sh          # seds ar9285_reset.c:636-801 + ah.c helpers out of
                         # /usr/src VERBATIM, compiles with HALASSERT compiled
                         # out (release kernel shape), -O2
    cc -O0 -o ar9285_harness_O0 ar9285_harness.c   # for mode 3
    sh run.sh            # modes 1, 2, 3
    sh build_fixed.sh    # applies fix.diff to a scratch copy (sys/ untouched),
                         # rebuilds, validates (mode 4 + mode 2 under fix)

## Expected output (baseline)

    mode 1: ... DF-4624 REPRODUCED: all 128 PDADC values sourced from
            pdadcValues[-1] (ar9285_reset.c:797 with k==0)
    mode 2: SIGSEGV si_addr=0x800473fff expected=0x800473fff (base-1)
            DF-4624 REPRODUCED: fault at exactly pdadcValues[-1]
    mode 3 (O0): poison=0xA7 -> minPower=0x0053 (=0xA7/2)
                 poison=0x12 -> minPower=0x0009 (=0x12/2)

## Fix validation

    build_fixed.sh output:
      minPower=0x7c7c ... pdadc[0]=0x00 ... (non-marker entries: 128)
      DF-4624 FIX VALIDATED: pdadc zeroed, no [-1] read, minPower sentinel
      0x7c7c preserved
      mode2 under fix: "no fault -- not reproduced"

## Files

ar9285_harness.c        harness (kernel function compiled verbatim via .inc)
build.sh / run.sh       exact build/run
build_fixed.sh          scratch-tree patch + rebuild + validation
fix.diff                the fix (early bail when numXpdGains == 0)
*.inc                   the verbatim extractions (build artifacts, kept)
*.log, env.txt          full untrimmed runs
