ar5413: unbounded kk pdadc walk writes past pcdacTable[PWR_TABLE_SIZE_2413=128] β intra-object heap OOB write corrupting RF bank state
Summary
The three write loops have no kk bound; pPDADCValues is pcdacTable[128], the first data member of the malloc'd state. With a crafted EEPROM (xpd_mask=0xF, Pmin pattern {0,58,58,58}, overlap 15) the extrapolation sums to 168 entries - 40 uint16 cells (80 bytes) past the array overwrite the RF bank scratch later programmed to the radio, and the tail backfill is skipped leaving a stale table. REPRODUCED on guest with the verbatim-extracted function against the faithful struct (first clobbered cell exactly at the array end, last 79 bytes past; fix validated -> no overflow, canary intact). Bound-all-three-loops fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4619 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| ar5413_harness.c | β | 11.9 KB | view raw | |
| build.sh | β | 66 B | view raw | |
| run.sh | β | 99 B | view raw | |
| build.log | β | 707 B | view raw | |
| run.log | β | 2.4 KB | view raw | |
| env.txt | β | 271 B | view raw | |
| VERDICT.md | β | 4.2 KB | β raw | |
| README.md | β | 2.6 KB | β raw | |
| fix.diff | β | 1.1 KB | view raw | |
| fix_build.log | β | 140 B | view raw | |
| fix_run.log | β | 1.3 KB | view raw | |
| verdict.json | β | 4.0 KB | view raw |
DF-4619 β ar5413 pdadc walk: unbounded kk overflows pcdacTable[128] (intra-object heap OOB write)
What
ar5413getGainBoundariesAndPdadcsForPowers() (sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:397-540)
writes the final power-detector ADC table through pPDADCValues[kk++] at three
sites (:497, :506, :520) with no bound on kk. pPDADCValues is
ahp->ah_pcdacTable (ar5413.c:554), i.e. priv->pcdacTable[PWR_TABLE_SIZE_2413]
with PWR_TABLE_SIZE_2413 == 128 (ah_eeprom_v3.h:282) β the first data member
of the heap-allocated struct ar5413State (ar5413.c:35-57, allocated at :777).
With a crafted EEPROM (AR_EEPROM_VER5_0 / eepMap==2 dataset; parse path
ah_eeprom_v3.c:823-824 β ar2413SetupRawDataset :633-675 β
ar2413EepromToRawDataset :678-728), the per-gain entry count
(extrapolate-below + in-table + extrapolate-above) sums to more than 128:
worst case gain0 = b[0]+overlap-Pmin[0], gain_i = 2*overlap + b[i]-b[i-1]
(both bounded by 63/15), total up to ~168-231 entries. The trailing
while (kk < 128) backfill (:531-534) is skipped once kk > 128, so the
tail of the table is left stale as well.
Result: pPDADCValues[128..] overwrites Bank1Data/Bank2Data/Bank3Data/Bank6Data
inside struct ar5413State β an intra-object heap overflow (stays inside the
ath_hal_malloc allocation; does not cross the slab) that corrupts the RF bank
scratch data programmed to the radio at ar5413Reset time
(ar5413SetRfRegs:249-253 use priv->Bank*Data) with EEPROM-influenced 16-bit
values. Triggered on every channel change / reset once the crafted dataset is
in the card's EEPROM.
Reproduce
On the DragonFly guest (no ath hardware needed β the vulnerable functions are pure C over EEPROM-shaped data):
cd findings/poc/DF-4619 ./build.sh # sed-extracts ar5413.c:288-540 VERBATIM from /usr/src, compiles ./ar5413_harness 1
Expected decisive lines (see run.log):
DF-4619 REPRODUCED: pPDADCValues write walked past pcdacTable[127]: first clobbered u16 cell 0 bytes past array end, last 79 bytes past (value 0x007f) => intra-object heap OOB WRITE past pcdacTable[PWR_TABLE_SIZE_2413=128] into Bank data
Fix validation
fix.diff bounds all three write loops with kk < PWR_TABLE_SIZE_2413.
Rebuilt from the patched file: mode 1 reports DF-4619: NO overflow detected
(bank canary 0xAAAAAAAA intact). See fix_run.log / VERDICT.md.
Threat model
Attacker supplies an AR5413-family card with crafted EEPROM calibration data (physical / supply-chain). Impact: kernel heap-object corruption (confined to the ar5413State allocation) β corrupted radio programming / malfunction, crash class. No path from unprivileged local user on the host.
DF-4619 VERDICT
Finding
Unbounded kk in ar5413getGainBoundariesAndPdadcsForPowers
(sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:397-540) writes past
pcdacTable[PWR_TABLE_SIZE_2413=128] (first data member of the
ath_hal_malloc-allocated struct ar5413State, ar5413.c:35-57,777; bound
constant ah_eeprom_v3.h:282; sink pPDADCValues = ahp->ah_pcdacTable
ar5413.c:554,793-794). Three unbounded write sites: :497 (extrapolate below),
:506 (main walk), :520 (extrapolate above). Bounded inputs from the EEPROM
parser: numVpd β {0,4,5} (ah_eeprom_v3.c:656-673), pwr_t4 β [0,244]
(pwr_I 5-bit Γ4 + 3-4 deltas Γ2Γ15, ah_eeprom_v3.c:715-721), overlap β€ 15
(AR_PHY_TPCRG5_PD_GAIN_OVERLAP = 0xF, ar5212phy.h:349), boundaries clamped
β€ 63 (ar5413.c:477-482). Worst-case total β 168 (constructed in harness; upper
bound analysis β 15+4Β·2Β·15+63+... β 231) > 128 β overflow.
Reproduction (guest, DragonFly 6.5-DEVELOPMENT x86_64, gcc 8.3)
Harness compiles the REAL function bodies verbatim β build.sh sed -n
'288,540p' /usr/src/sys/.../ar5413.c β against a faithful struct
(member order from ar5413.c:35-57; bank row counts 1/1/1/37/3 counted from the
shipped ar5212.ini; verified offsets: pcdacTable@72, Bank1Data@328,
vpdTable_I@1524, sizeof=2040). Dataset crafted in the exact shape
ar2413SetupRawDataset/ar2413EepromToRawDataset produce for xpd_mask=0xF,
single pier, Pmin pattern {0,58,58,58}, overlap 15, spans β₯2:
mode=1 numPdGainsUsed(ret)=4 DF-4619 REPRODUCED: ... first clobbered u16 cell 0 bytes past array end, last 79 bytes past (value 0x007f)
i.e. 40 uint16 cells (80 bytes) beyond pcdacTable[127] were written by the
kernel code, landing in Bank1Data..Bank6Data. This is an intra-object
overflow: it stays inside the 2040-byte ar5413State allocation (banks occupy
offsets 328-500; ~1.5KB of vpd tables follow), so it does NOT cross the slab β
it corrupts the RF bank scratch buffers later programmed to hardware
(ar5413.c:249-253) and the vpd tables. By C semantics it is an out-of-bounds
write (CWE-787); by kernel impact it is object-state corruption (garbage radio
programming) rather than adjacent-object smashing.
uid=0?
No. Exploitation is gated on supplying an AR5413 card with crafted EEPROM (physical access / supply chain); even then the write is intra-object with constrained 16-bit values (clamped pdadc range 0..127 at two of three sites; :506 propagates VpdTable_I values β€ ~0x3FF-derived). The realistic ceiling is malfunction/panic, not control-flow hijack on this guest's hardware (absent). Impact recorded as dos.
Fix validation
fix.diff adds && kk < PWR_TABLE_SIZE_2413 to the three write loops.
Baseline (pristine /usr/src copy): reproduced as above.
Patched (pristine + all three batch fixes applied, re-extracted, rebuilt):
DF-4619: NO overflow detected β bank canary region 0xAAAAAAAA intact,
exit=1 from the "no overflow" detector (expected). See fix_run.log.
Negative results established while hunting (pass 2)
- vpdTable_L/R/I ROW overflow (span > 64) is IMPOSSIBLE: Pmin_t2 =
min(first_L,first_R)/2 and Pmax_t2 = min(last_L,last_R)/2 (ar5413.c:435-445)
while each channel's pwr_t4 list is non-decreasing (deltas β₯ 0, 4-bit,
ah_eeprom_v3.c:719) and Ξ£deltas β€ 4Β·15 β span β€ 60 < 64 = row width. The
<=off-by-one at :353 tops out at index 60. KILLED. - numVpd > 5 OOB: parser hardcodes {0,4,5} (ah_eeprom_v3.c:660-671). KILLED.
- ar5413GetChannelMaxMinPower OOB: the freq > data[numChannels-1] pre-check (:716-727) pins the interpolation loop (:730) to i β€ numChannels-1. KILLED.
- ar5413SetChannel freq underflow (freq<2192): unreachable via the net80211 channel table; results masked to 8 bits before register writes. KILLED.
- interpolate_signed div-by-zero guarded (:384). ar5413GetRfBank default NULL handled. GetMinPower/GetMaxPower pwr_t4[numVpd-1] in-bounds ({0,4,5}).
- DF-1766 (numChannels==0 guard) β known pass-1 finding, not re-reported.
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
fixedfix.diff bounds the three pPDADCValues write loops with kk < PWR_TABLE_SIZE_2413; on the patched extraction mode 1 reports 'NO overflow detected' with the bank canary region intact (baseline reproduced 168-cell walk, 80 bytes past).
fix_run.log (PATCHED BUILD OK section): '--- mode 1 ... DF-4619: NO overflow detected'
Confirmed kernel references
- sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:397
- sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:495
- sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:506
- sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:520
- sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:531
- sys/dev/netif/ath/ath_hal/ar5212/ar5413.c:554
- sys/dev/netif/ath/ath_hal/ar5413.c:35
- sys/dev/netif/ath/ath_hal/ar5413.c:777
- sys/dev/netif/ath/ath_hal/ah_eeprom_v3.h:282
- sys/dev/netif/ath/ath_hal/ah_eeprom_v3.c:656
Detail
Exploit chain
crafted EEPROM (AR5EPROM_VER5_0/eepMap=2, xpd_mask=0xF, flat-ish curves, Pmin {0,58,58,58}) -> ath attach reads cal dataset -> first channel set invokes ar5413SetPowerTable:577 -> getGainBoundariesAndPdadcsForPowers kk walk exceeds 128 -> pPDADCValues[128..167] overwrite Bank1/2/3/6Data -> corrupted RF banks programmed to the radio at next ar5413SetRfRegs (ar5413.c:249-253) + stale pdadc tail. No uid0 route: hardware-gated, intra-object, clamped 16-bit values.
Evidence (decisive lines)
run.log: lines 'DF-4619 REPRODUCED: ... first clobbered u16 cell 0 bytes past array end, last 79 bytes past'; fix_run.log: 'DF-4619: NO overflow detected' after patching; ar5413_harness.c + build.sh show the verbatim extraction from /usr/src.
PoC changes
harness written from scratch (no seed PoC): sed-extracts ar5413.c:288-540 verbatim at build time on the guest, faithful struct (bank rows 1/1/1/37/3 from ar5212.ini, verified offsets pcdac@72 Bank1@328 vpdI@1524 sizeof 2040), HALASSERT compiled out to mirror release (AH_ASSERT undefined), dataset crafted per ar2413SetupRawDataset/ar2413EepromToRawDataset shapes; spans >=2 chosen to isolate DF-4619 from the independent DF-4622 wrap
Verified recommended fix
Bound all three pPDADCValues write loops with kk < PWR_TABLE_SIZE_2413 (see fix.diff).
Verdict
REPRODUCED on the DragonFly guest by compiling the verbatim kernel function bodies (sed-extracted ar5413.c:288-540 from /usr/src) against a faithful ar5413State layout: with an EEPROM-shaped crafted dataset (xpd_mask=0xF, Pmin pattern {0,58,58,58}, overlap 15) the unbounded kk walk at ar5413.c:497/:506/:520 wrote 40 uint16 cells (80 bytes) past pcdacTable[PWR_TABLE_SIZE_2413=128] into the Bank1Data..Bank6Data members of the heap-allocated ar5413State object (intra-object overflow; RF bank scratch corrupted). Exploitation is gated on attacker-supplied AR5413 hardware with crafted EEPROM (physical/supply-chain); realistic ceiling is radio misprogramming/crash, not privilege escalation. Fix validated: with kk<PWR_TABLE_SIZE_2413 guards the overflow is gone (bank canary intact).
No comments yet.