{
  "finding_id": "DF-4624",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "REPRODUCED on the DragonFly guest (verbatim-extraction harness): with a v4k EEPROM shape of xpdGain==0 (ar9285_reset.c:580 xpdMask, loop :593-602 leaves numXpdGain==0; the :595 HALASSERT only guards >2 gains, never 0), ar9285GetGainBoundariesAndPdadcs executes the tail backfill with k==0 -- ar9285_reset.c:797 pPDADCValues[k] = pPDADCValues[k-1] reads pdadcValues[-1], 1 byte below the callers static pdadcValues[128] (:570). Proven three ways: (1) adjacency flavor, a 0x5A marker placed at pdadc[-1] propagates into ALL 128 PDADC entries (run.log mode 1, non-marker entries: 0); (2) fault flavor, SIGSEGV si_addr == array base-1 exactly (run.log mode 2); (3) the byte is then programmed to the PHY via ar5416WritePdadcValues (:628). Additionally ar9285_reset.c:719 reads uninitialized stack minPwrT4[0]: GCC warns on the verbatim extraction, and at -O0 a 128KB stack spill of 0xA7 yields *pMinCalPower==0x0053 and 0x12 yields 0x0009 (exactly poison/2, run_O0_mode3.log) -- uninit stack data flows out to the caller. In-kernel the [-1] read hits whatever precedes the .bss/.data static (adjacent kernel data), so a crafted-EEPROM AR9285 dongle programs the PHY pdadc table from an out-of-bounds kernel byte on every channel set; no exfil channel (register-only sink), realistic ceiling is radio misprogramming/correctness, not privilege escalation. Fix validated (early bail when numXpdGains==0 + pdadc zeroing): no [-1] read (marker untouched, pdadc zeroed), no uninit read (minPower sentinel preserved), guard-page flavor faults no longer occur.",
  "exploit_chain": "crafted v4k EEPROM xpdGain==0 -> ar9285SetPowerCalTable :580/:593-602 numXpdGain=0 -> ar9285GetGainBoundariesAndPdadcs :719 uninit minPwrT4[0] read and :796-798 backfill with k==0 -> pdadcValues[-1] 1-byte OOB read (kernel static adjacent data) replicated into all 128 PDADC values -> ar5416WritePdadcValues :628 programs the OOB byte into the AR9285 PHY power table; also gainBoundaries backfilled with defaults (58). Hardware-gated (needs an AR9285/AR9271 device whose EEPROM says xpdGain==0); no uid0 route.",
  "evidence": "run.log mode 1 DF-4624 REPRODUCED: all 128 PDADC values sourced from pdadcValues[-1] (ar9285_reset.c:797 with k==0); run.log mode 2 SIGSEGV si_addr=0x800473fff expected=0x800473fff (base-1); run_O0_mode3.log poison=0xA7: minPower=0x0053 ... poison=0x12: minPower=0x0009; build.log contains GCCs own -Wuninitialized minPwrT4[0] warning on the verbatim extraction; fix_build.log DF-4624 FIX VALIDATED: pdadc zeroed, no [-1] read, minPower sentinel 0x7c7c preserved",
  "kernel_refs": [
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:570",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:580",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:593",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:607",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:612",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:628",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:636",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:683",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:719",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:722",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:791",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:796",
    "sys/dev/netif/ath/ath_hal/ar9002/ar9285_reset.c:797",
    "sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.h:93",
    "sys/dev/netif/ath/ath_hal/ah_internal.h:665"
  ],
  "poc_changes": "no seed PoC; written from scratch as a verbatim-extraction harness (build.sh seds ar9285_reset.c:636-801 and the ah.c helpers out of the guests /usr/src); HALASSERT compiled out to mirror release kernels; pdadcValues adjacency modeled by page-placing the array with a marker byte / PROT_NONE guard below; uninit-stack proof needs the -O0 build (the -O2 constprop folds minPwrT4[0] to 0 -- GCCs warning is kept as corroboration)",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 150,
  "guest_dirty": 0,
  "build_cmd": "sh build.sh  (sed -n 636,801p /usr/src/sys/.../ar9285_reset.c > ar9285_extracted.inc; sed ah.c 1264-1305/1313-1340/1347-1360 > ah_extracted.inc; cc -O2)",
  "run_cmd": "sh run.sh   (./ar9285_harness 1|2 ; cc -O0 ...; ./ar9285_harness_O0 3)",
  "code_hash": "30150b63d346a2f639bbb10942e03e2d62131fdb7268d402b27f6ad463678e23",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 x86_64 (harness-level validation of the patched extraction; full kernel-build validation hardware-gated: no AR9285 on the guest)",
  "fix_verdict": "fix.diff inserts an early bail when numXpdGains==0 (zero the pdadc table and return) right after ar5416GetChannelCenters in ar9285GetGainBoundariesAndPdadcs; on the patched extraction mode 4 shows pdadc zeroed with the 0x5A marker never read, minPower sentinel 0x7c7c preserved (the :719 uninit read never executes), and the guard-page flavor no longer faults.",
  "fix_evidence": "fix_build.log: minPower=0x7c7c gainBoundaries[0]=0 pdadc[0]=0x00 pdadc[127]=0x00 (non-marker entries: 128) + DF-4624 FIX VALIDATED + mode2-under-fix no fault -- not reproduced",
  "notes": "ar5413 sibling of this same trigger is DF-4620 (uint32 wrap, +8.6GB wild reads); ar9285 uses int counters so the index is merely -1. Distinct from known DF-1768 (pwrPdg delta -> FillVpdTable overflow + sizeCurrVpdTable uint8 wrap at :766, lines 650-782) and DF-1769 (chain-loop OOB at :607-612): different trigger (xpdGain==0), different lines (:719, :796-798), different primitive.",
  "recommended_fix": "Early-return in ar9285GetGainBoundariesAndPdadcs when numXpdGains==0 (zero the PDADC table); optionally fail ar9285SetPowerCalTable when the EEPROM xpdMask==0 (see fix.diff)."
}