Flat pd-gain curve makes sizeCurrVpdTable==0 β VpdTable_I[ii][sizeCurrVpdTable-1/-2] uint32-wrap wild reads at row+0x1FFFFFFFE (twin of DF-4622/4650/4662)
Summary
Equal pwr_t4 points (a legal flat calibration, no malformed data) give span 0, so the [-1]/[-2] subscripts wrap - reads at row+0x1FFFFFFFE (harness SIGSEGV si_addr exact-match asserted). Guard>=2 fix (validated).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4682 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 2.2 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 | β | 1.1 KB | 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 | β | 859 B | view raw | |
| verdict.json | β | 3.8 KB | view raw |
DF-4682 β ar2317: flat pd-gain curve β sizeCurrVpdTable-1/-2 uint32 wrap
Twin of DF-4622 (ar5413) / DF-4650 (ar2316) / DF-4662 (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 3
Expected (baseline)
REPRODUCED: SIGSEGV si_addr=0xa00475dfe expected wild READ address 0xa00475dfe (uint32 index wrap ...) MATCH: wild OOB read proven (ar2317.c uint32 counter underflow)
A flat EEPROM power curve (all pwr_t4 deltas zero β Pmax_t2 == Pmin_t2)
makes sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii] (ar2317.c:438, uint32
arithmetic) zero; the Vpd_step computation at :445-446 then reads
VpdTable_I[ii][sizeCurrVpdTable-1] and [sizeCurrVpdTable-2] β element
indices 0xFFFFFFFF / 0xFFFFFFFE β wild reads at row + 0x1FFFFFFFE (+8.59
GB). Left unmapped in userspace, the harness faults at exactly the predicted
address. No malformed EEPROM is even required β degenerate flat cal curves
suffice. (:427 also reads VpdTable_I[ii][1], left uninitialized in this
shape β side observation.)
Fix validation
fix.diff guards the [-1]/[-2] reads with sizeCurrVpdTable >= 2
(Vpd_step=1 fallback) and clamps the extrapolate-above last index. Patched
mode 3 (fix_run.log): completes with sane minPower=0x003e, no SIGSEGV.
Not re-reported: DF-1806 (numChannels==0), DF-4681 (zero-gain) β different triggers, different guards.
DF-4682 β VERDICT
Status: REPRODUCED (guest, verbatim-extraction harness, mode 3) Β· impact: dos Β· confidence: certain Β· fix: fixed (validated)
Root cause
sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii]; (ar2317.c:438) is uint32_t
arithmetic over int16_t values. An EEPROM curve whose pwr_t4 points are all
equal (flat calibration β two identical pier powers, delta fields 0) yields
Pmax_t2 == Pmin_t2, so sizeCurrVpdTable == 0, and:
Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] - /* [0xFFFFFFFF] */
VpdTable_I[ii][sizeCurrVpdTable-2]); /* [0xFFFFFFFE] */
(ar2317.c:445-446) β wild reads at row + 0x1FFFFFFFE bytes. The same
wrap pattern repeats in the extrapolate-above block (:455). Additionally
:427 reads VpdTable_I[ii][1], which ar2317FillVpdTable left uninitialized
when the span is 0 (its while (ii <= 0) writes only index 0).
Verification (run.log mode 3)
Four gains, numVpd=4, pwr_t4 all 124 β Pmin=Pmax=62, sizeCurrVpdTable=0. The
harness leaves the wild target unmapped: SIGSEGV with
si_addr == &vpdTable_I[0][0] + 0x1FFFFFFFE exactly (asserted in the
handler; run.log shows the MATCH line). In-kernel the same address hits the
direct map (garbage Vpd_step β extrapolated pdadc values β pcdacTable β 32
PHY writes, :566-573) or a KVA hole (panic).
Flat curves are the mildest trigger of the three ar2317 siblings β no malformed EEPROM needed, just equal calibrated power points.
Fix validation (fix_run.log, fixC section)
fix.diff: if (sizeCurrVpdTable >= 2) { ...existing Vpd_step math... } else
Vpd_step = 1; plus vpdLastIdx = (sizeCurrVpdTable >= 1) ? sizeCurrVpdTable-1
: 0 for the extrapolate-above reference. Patched mode 3 completes with
minPower=0x003e (= 62, the flat curve's Pmin β sane) and no fault. git apply
--check clean; 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.
Fix verification
fixedValidated on the guest: fix.diff (sizeCurrVpdTable >= 2 guard around the [-1]/[-2] Vpd_step reads with a Vpd_step=1 fallback, and a clamped vpdLastIdx for the extrapolate-above reference) applied to a copy of the pristine guest ar2317.c, function bodies re-extracted verbatim, harness rebuilt and re-run. Baseline SIGSEGVs at the wild address; patched mode 3 completes without fault (minPower=0x003e sane). fix.diff is git-apply-able against the pristine tree and was never applied to sys/.
fix_run.log ('===== fixC' section: PATCHES_APPLIED, FIXED_BUILD_OK, 'no SIGSEGV: function completed') vs run.log mode 3; fix.diff in pack.
Confirmed kernel references
Detail
Exploit chain
crafted (or merely degenerate-flat) v3 EEPROM power curves on AR2317 card -> sizeCurrVpdTable==0 -> VpdTable_I[ii][0xFFFFFFFF/0xFFFFFFFE] wild reads at row+0x1FFFFFFFE -> direct-map garbage propagates into Vpd_step -> extrapolated pdadc values -> pcdacTable + 32 PHY register writes (ar2317.c:566-573) = radio misprogramming, or KVA-hole kernel panic. No uid0 route.
Evidence (decisive lines)
run.log mode 3: 'REPRODUCED: SIGSEGV si_addr=0xa00475dfe' == 'expected wild READ address 0xa00475dfe' + 'MATCH: wild OOB read proven'; fix_run.log ('===== fixC' section): patched mode 3 completes with minPower=0x003e, no SIGSEGV.
PoC changes
Same harness as DF-4680 (mode 3); no kernel-code changes.
Verified recommended fix
Guard the sizeCurrVpdTable-1/-2 reads with a sizeCurrVpdTable >= 2 check (Vpd_step=1 fallback) and clamp the extrapolate-above last index (fix.diff).
Verdict
REPRODUCED on the DragonFly guest (verbatim-extraction harness, mode 3): with 4 pdGains whose EEPROM power curves are FLAT (all pwr_t4 deltas zero -> Pmax_t2 == Pmin_t2), sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii] (ar2317.c:438, uint32 arithmetic of int16 values) is 0, and the Vpd_step computation at ar2317.c:445-446 reads VpdTable_I[ii][sizeCurrVpdTable-1] and [sizeCurrVpdTable-2] -- element indices 0xFFFFFFFF and 0xFFFFFFFE under uint32 arithmetic -- i.e. wild reads at row+0x1FFFFFFFE bytes (+8.59 GB). Left unmapped, the harness faulted with SIGSEGV si_addr == &vpdTable_I[0][0] + 0x1FFFFFFFE exactly (asserted in the handler); in-kernel the same address is a direct-map garbage read (value flows into Vpd_step and the extrapolation above, then into pcdacTable and PHY registers) or a KVA-hole panic. Side observation: line 427 also reads VpdTable_I[ii][1] which FillVpdTable left uninitialized in this shape (intra-row uninit read). Flat curves require no EEPROM 'error' -- equal calibrated pwr points produce them -- making this the easiest trigger of the three ar2317 siblings. Twin of DF-4622 (ar5413) / DF-4650 (ar2316) / DF-4662 (ar2413). Fix validated (sizeCurrVpdTable>=2 guard + clamped last-index): completes with no fault.
No comments yet.