# DF-4683 — VERDICT

**Status: REPRODUCED (guest, verbatim-extraction harness, modes 4+5) ·
impact: dos (kernel heap corruption) · confidence: certain · fix: fixed
(validated)**

## Root cause

Two loops write into `priv->vpdTable_{L,R,I}[pdGainIdx][ii]` — rows are
`MAX_PWR_RANGE_IN_HALF_DB = 64` uint16 entries (ah_eeprom_v3.h:291) inside
the heap-allocated `struct ar2317State` (last three members; `vpdTable_I` is
the final member):

1. `ar2317FillVpdTable()` — `while (ii <= (uint16_t)(Pmax - Pmin))`
   `retVpdList[pdGainIdx][ii] = kk;` (ar2317.c:289-306). The only sanity
   check inside is `numIntercepts < 2` (:286); **the span `Pmax - Pmin` is
   never validated against the row width, and `Pmax < Pmin` is never
   rejected.**
2. the interpolate loop — `for (kk = 0; kk < (uint16_t)(Pmax_t2[..] -
   Pmin_t2[..]); kk++) VpdTable_I[numPdGainsUsed][kk] = ...`
   (ar2317.c:393-398), same missing clamp.

`Pmin_t2/Pmax_t2` derive straight from EEPROM bytes at :372-382
(`pwr_t4` quarters of dBm, `/2` → half dB): descending lists give
`Pmax < Pmin` (wrap trigger), wide lists give spans up to 127 (> 64; plain
clamp trigger). No sorting/monotonicity validation exists anywhere on the
v3-EEPROM raw-data path.

## Verified behavior (run.log)

The ar2317State is emulated as the kernel heap allocation: mmap'd, struct end
placed exactly at a page boundary, PROT_NONE guard page after.

- **Mode 4 (wrap)** — gain slot 3 with pwr_t4 {180,176,172,168} →
  Pmin_t2=90, Pmax_t2=84 → `(uint16_t)(84-90) = 65530` → the loop starts
  storing at `vpdTable_L[0][0]` and marches; SIGSEGV **on the STORE** at
  exactly the struct end (si_addr == guard base; the faulting instruction is
  the `retVpdList[...] = kk` store — the reads at that point target the
  mapped dataset). Proven escape of a would-be 131060-byte sequential
  overwrite with Vpd-derived values.
- **Mode 5 (clamp, no wrap)** — gain slot 0 with pwr_t4 {0,68,136,204}
  (perfectly valid ascending data, span 102): FillVpdTable row-3 indices
  64..102 spill intra-object (vpdTable_R/vpdTable_I heads — observed canary
  clobber), then the interpolate loop writes `VpdTable_I[3][64..101]`,
  i.e. past the object end; SIGSEGV at exactly `&vpdTable_I[3][64]` ==
  struct end. **Valid EEPROM data, no integer wrap, pure missing clamp.**

In-kernel: writes continue into adjacent kernel heap objects
(`ath_hal_malloc` = kmalloc) for up to ~128 KB (mode 4) — adjacent-object
corruption with semi-controlled content, or immediate panic on unmapped KVA.
This is an OOB **write beyond the heap object**, a class stronger than the
intra-object spill (DF-4680) and wild reads (DF-4681/4682) filed for the
family — hence Medium, memcorrupt bucket.

## Why the family twins did not cover it

DF-4619/4620/4622 (ar5413), DF-4648/4649/4650 (ar2316), DF-4660/4661/4662
(ar2413) each filed the three specified twins only. The span defect is
verbatim-present in all five ar5212 RF files (`ar2317.c:289 == ar2413.c:309
== ar2316.c:312 == ar5413.c:353 == ar2425.c:311`); this is its first filing
(on ar2317, as instructed). Recommend mirroring the fix to the other four.

## Fix validation (fix_run.log)

`fix.diff` (fixD): `continue` (skip gain) when `Pmax_t2 < Pmin_t2` or
`(Pmax_t2 - Pmin_t2) >= MAX_PWR_RANGE_IN_HALF_DB`, placed after the
Pmin/Pmax computation and before the FillVpdTable/interpolate calls.
- Mode 5 with fixD alone: completes, numPdGainsUsed=3 (oversized gain
  skipped), no fault.
- Mode 4 with fixD+fixB (fixB = DF-4681's `numPdGainsUsed==0` early return,
  included as `fixB.diff`): completes, ret=0, sentinel minPower — the
  residual zero-gain path created by skipping the only gain is covered by
  DF-4681's guard, as documented.
`git apply --check` clean against `sys/`; never applied to `sys/`.

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