numPdGainsUsed==0 (EEPROM xpd_mask==0): uint32-wrapped wild reads at array+0x1FFFFFFFE (+8.59 GB) in the backfill loops + uninitialized Pmin_t2[0] stack read (twin of DF-4620/4649/4661)
Summary
xpd_mask==0 leaves zero gains; the compiled-out HALASSERT lets execution continue into uninit Pmin read and [ii-1]/[kk-1] wraps (uint32 counters at 0) - wild reads at +8.59 GB propagating into TPCRG5 and 32 PHY writes. Harness: wild pages seeded -> propagation observed; minPower run-to-run variance proves uninit. Early-return + caller-check fix (validated).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4681 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.9 KB | β raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| ar2317_harness.c | β | 16.5 KB | view raw | |
| build.sh | β | 85 B | view raw | |
| run.sh | β | 99 B | view raw | |
| fix_validate.sh | β | 2.8 KB | view raw | |
| fix.diff | β | 699 B | view raw | |
| build.log | β | 686 B | view raw | |
| run.log | β | 3.0 KB | view raw | |
| fix_run.log | β | 8.6 KB | view raw | |
| env.txt | β | 844 B | view raw | |
| manifest.json | β | 881 B | view raw | |
| verdict.json | β | 4.1 KB | view raw |
DF-4681 β ar2317: numPdGainsUsed==0 β uint32-wrapped wild reads + uninit stack
Twin of DF-4620 (ar5413) / DF-4649 (ar2316) / DF-4661 (ar2413), verified for
sys/dev/netif/ath/ath_hal/ar5212/ar2317.c.
Build
Same harness as DF-4680: cd /root/df2317 && sh build.sh
Run
./ar2317_harness 2 sh run.sh # includes 3 extra runs showing minPower uninit variance
Expected (baseline)
mapped wild pages ... seeded 0x5A5A gainBoundaries[0] = 0x5a5a pcdacTable[0] = 0x5a5a minPower out = 0xd4a0 (varies run-to-run) DF-4681 REPRODUCED: uint32-underflow wild reads at ar2317.c:465/:469 + uninit stack read at :405
With an EEPROM whose xpd_mask is 0 (all numVpd == 0, the exact dataset shape
the shared SetupRawDataset produces, ah_eeprom_v3.c:656-673),
numPdGainsUsed stays 0; HALASSERT(1 <= numPdGainsUsed ...) at :516 is
compiled out in release (ah_internal.h:665-676) and the caller (:513-515)
ignores the return, so:
- :465
pPdGainBoundaries[ii-1]withii==0(uint32) β element 0xFFFFFFFF β read atgb + 0x1FFFFFFFE(+8.59 GB) - :469
pPDADCValues[kk-1]withkk==0β same wrap from pcdacTable - :405
*pMinCalPower = Pmin_t2[0]reads uninitialized stack
The harness maps the exact wild pages (kernel-direct-map emulation), seeds
0x5A5A, and shows both values propagate into outputs that reach hardware:
gainBoundaries[0] β AR_PHY_TPCRG5 (ar2317SetPowerTable:575-580),
pcdacTable[0] β 32 PHY register writes (:566-573).
Fix validation
fix.diff = early return 0 when numPdGainsUsed==0 (before the Pmin_t2[0]
read) + numPdGainsUsed < 1 β AH_FALSE in ar2317SetPowerTable. Patched mode 2
(fix_run.log): ret=0, minPower=0x7c7c (sentinel), gainBoundaries[0]=0x0000,
pcdacTable[0]=0xaaaa (canary intact) β no wild reads, no uninit read.
Not re-reported: DF-1806 (numChannels==0, pass 1) β different trigger/guard.
DF-4681 β VERDICT
Status: REPRODUCED (guest, verbatim-extraction harness, mode 2) Β· impact: dos Β· confidence: certain Β· fix: fixed (validated)
Root cause
ar2317getGainBoundariesAndPdadcsForPowers() only increments
numPdGainsUsed when a gain has numVpd > 0 (ar2317.c:366-403). An EEPROM
with xpd_mask == 0 yields all-zero numVpd (shared SetupRawDataset,
ah_eeprom_v3.c:656-673), leaving numPdGainsUsed == 0, yet the function
continues:
- :405
*pMinCalPower = Pmin_t2[0];βPmin_t2[]was never written: uninitialized stack read, value flows intoah_txPowerIndexOffset(ar2317SetPowerTable:559-562). Observed run-to-run variance in the harness: 0xd4a0 / 0xd570 / 0xd3c0 / 0xd470. - :464-467
while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1];withii==0:iiisuint32_t, soii-1= 0xFFFFFFFF β read atgb + 0x1FFFFFFFEbytes (+8.59 GB) β a wild read, not a 2-byte under-read. - :468-471
while (kk < 128) pPDADCValues[kk] = pPDADCValues[kk-1];withkk==0β identical wrap from the pcdacTable base.
:516's HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3) is a no-op in
release kernels (AH_ASSERT undefined; ah_internal.h:665-676 β the guest
GENERIC is such a build), and the caller (ar2317.c:513-515) never checks the
return value.
Verification (run.log mode 2)
Direct-map survival emulation: the harness mmaps the two exact wild pages
(gb+0x1FFFFFFFE, pcdacTable+0x1FFFFFFFE), seeds 0x5A5A, and the call
completes, with both wild values propagating into hardware-facing
outputs (gainBoundaries[0] β AR_PHY_TPCRG5 at :575-580; pcdacTable[0] β
32 PHY register writes at :566-573). The complementary fault case (same wrap
arithmetic, unmapped target) is proven by mode 3/DF-4682's SIGSEGV at the
predicted wild address. In-kernel: direct-map garbage (radio misprogrammed
from wild-read values) or KVA-hole panic.
Fix validation (fix_run.log, fixB section)
fix.diff: if (numPdGainsUsed == 0) return 0; before the Pmin_t2[0] read,
plus a numPdGainsUsed < 1 β AH_FALSE guard in ar2317SetPowerTable.
Patched: ret=0, sentinel minPower, gb[0]=0x0000, pcdac[0]=0xaaaa β clean.
git apply --check clean; never applied to sys/.
Distinction from known findings
DF-1806 (pass 1) is the numChannels==0 β idxL/idxR wild-read bug: different trigger (missing piers vs zero gains), different guard. DF-4682 is the same uint32-underflow class at :445-446 via flat curves β filed separately.
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.
Fix verification
fixedValidated on the guest: fix.diff (numPdGainsUsed==0 early return 0 before the Pmin_t2[0] read, plus a numPdGainsUsed<1 -> AH_FALSE check in ar2317SetPowerTable) applied to a copy of the pristine guest ar2317.c, function bodies re-extracted verbatim, harness rebuilt and re-run. Baseline propagates the 0x5A5A wild-read values; patched build returns 0, leaves minPower at the 0x7c7c sentinel, gainBoundaries[0]=0x0000 and pcdacTable[0]=0xaaaa (canary untouched): no wild reads, no uninit read. fix.diff is git-apply-able against the pristine tree and was never applied to sys/.
fix_run.log ('===== fixB' section: PATCHES_APPLIED, FIXED_BUILD_OK, ret=0/sentinel/no propagation) vs run.log mode 2; fix.diff in pack.
Confirmed kernel references
- sys/dev/netif/ath/ath_hal/ar5212/ar2317.c:405
- sys/dev/netif/ath/ath_hal/ar5212/ar2317.c:464-467
- sys/dev/netif/ath/ath_hal/ar5212/ar2317.c:468-471
- sys/dev/netif/ath/ath_hal/ar5212/ar2317.c:513-516
- sys/dev/netif/ath/ath_hal/ar5212/ar2317.c:575-580
- sys/dev/netif/ath/ath_hal/ah_eeprom_v3.c:656
- sys/dev/netif/ath/ath_hal/ah_internal.h:665
Detail
Exploit chain
crafted v3 EEPROM (xpd_mask=0) on AR2317 card -> numPdGainsUsed==0 -> uint32-wrapped wild reads at gb+0x1FFFFFFFE and pcdacTable+0x1FFFFFFFE (direct-map garbage or KVA-hole panic) + uninit Pmin_t2[0] stack read -> garbage TPCRG5/pdadc programming or kernel panic. No uid0 route.
Evidence (decisive lines)
run.log mode 2: 'gainBoundaries[0] = 0x5a5a', 'pcdacTable[0] = 0x5a5a', 'DF-4681 REPRODUCED'; variance section: minPower 0xd570/0xd3c0/0xd470 (uninit variance); fix_run.log patched mode 2: ret=0, sentinel minPower 0x7c7c, gb[0]=0x0000, pcdac[0]=0xaaaa (canary intact, no propagation).
PoC changes
Same harness as DF-4680 (mode 2); no kernel-code changes.
Verified recommended fix
Early-return 0 when numPdGainsUsed==0 before the Pmin_t2[0] read, plus a caller-side numPdGainsUsed==0 -> AH_FALSE check in ar2317SetPowerTable (fix.diff).
Verdict
REPRODUCED on the DragonFly guest (verbatim-extraction harness, mode 2): with an EEPROM-shaped zero-gain dataset (xpd_mask=0 -> all numVpd=0, the exact shape the shared SetupRawDataset produces at ah_eeprom_v3.c:656-673) the function's backfill loops index with uint32 counters at 0: pPdGainBoundaries[ii-1] (ar2317.c:465) and pPDADCValues[kk-1] (ar2317.c:469) wrap to element index 0xFFFFFFFF and read at array+0x1FFFFFFFE bytes (+8.59 GB) -- the direct-map survival case proven by mapping the exact wild pages and observing both 0x5A5A values propagate into gainBoundaries[0] (-> AR_PHY_TPCRG5 by ar2317SetPowerTable:575-580) and pcdacTable[0] (-> 32 PHY reg writes, ar2317.c:566-573); the identical wrap arithmetic proven to fault in mode 3 (DF-4682). Plus uninitialized-stack read of Pmin_t2[0] at ar2317.c:405 (minPower varies run-to-run: 0xd4a0/0xd570/0xd3c0/0xd470). In-kernel this is a wild KVA read -> panic (KVA hole) or garbage radio programming; HALASSERT(:516) is compiled out in release and the caller (:513-515) never checks the return. Twin of DF-4620 (ar5413) / DF-4649 (ar2316) / DF-4661 (ar2413). Fix validated (early return + caller check): no wild reads, no uninit read.
No comments yet.