ar2316: unbounded kk write in getGainBoundariesAndPdadcsForPowers overflows pcdacTable[128] into RF bank scratch (twin of DF-4619/ar5413)
Summary
The three pPDADCValues write loops run a uint32 kk with no bound while the sink is the 128-entry pcdacTable member. EEPROM-shaped curves (xpd_mask=0xF, Pmin {0,58,58,58}, register-realistic overlap 15) walk kk to 168: 80 bytes past the array into the RF bank scratch of the malloc'd struct (intra-object heap overflow, offsets verified), later programmed to the radio; the tail backfill is skipped leaving a stale table. VERBATIM-EXTRACTION harness on guest REPRODUCES (kk ran to 168, canary clobbered 0..79 bytes past; fix validated -> no overflow). Bound-all-three-loops fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4648 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.5 KB | β raw | |
| VERDICT.md | β | 4.6 KB | β raw | |
| ar2316_harness.c | β | 12.4 KB | view raw | |
| build.sh | β | 191 B | view raw | |
| run.sh | β | 99 B | view raw | |
| build.log | β | 632 B | view raw | |
| run.log | β | 1.4 KB | view raw | |
| run.2.log | β | 1.4 KB | view raw | |
| env.txt | β | 346 B | view raw | |
| fix.diff | β | 1.2 KB | view raw | |
| fix_validate.sh | β | 1.3 KB | view raw | |
| fix_run.log | β | 3.3 KB | view raw |
DF-4648 β ar2316 unbounded kk write (twin of DF-4619/ar5413)
PoC: verbatim-extraction harness proving the heap OOB write past
pcdacTable[128] inside the ath_hal_malloced struct ar2316State.
Build (on the DragonFly guest, as root)
cd /root/df2316 && sh build.sh
# awk-extracts ar2316.c:251-499 verbatim from the pristine /usr/src tree
# into ar2316_extracted.inc (249 lines) and compiles:
# cc -O2 -Wall -Wno-pointer-sign -o ar2316_harness ar2316_harness.c
Run
./ar2316_harness 1 # DF-4648 (this finding)
./ar2316_harness 2 # DF-4649 (numPdGainsUsed==0 wild reads)
./ar2316_harness 3 # DF-4650 (flat-curve [size-1]/[-2] wrap)
# or: sh run.sh # all three
Expected output (mode 1)
sizeof(struct ar2316State)=2008 offsetof(pcdacTable)=72 Bank1Data=328 vpdTable_I=1492 mode=1 numPdGainsUsed(ret)=4 minPower=0x0000 DF-4648 REPRODUCED: pPDADCValues write walked past pcdacTable[127]: first clobbered u16 cell 0 bytes past array end, last 79 bytes past (value 0x007f) => kk ran to 168 => intra-object heap OOB WRITE past pcdacTable[PWR_TABLE_SIZE_2316=128] into Bank data
Exit code 0 = reproduced; with fix.diff applied (see fix_validate.sh)
mode 1 prints DF-4648: NO overflow detected and exits 1.
Threat model
Crafted EEPROM on an AR2316 (Atheros SoC 802.11) card: xpd_mask=0xF, Pmin pattern {0,58,58,58}, TPCRG5 overlap=15 (4-bit field). First channel set after attach walks kk to 168 and writes 80 bytes past the 128-entry table into the RF bank scratch. Hardware-gated; DoS-class.
DF-4648 β ar2316: unbounded kk write in ar2316getGainBoundariesAndPdadcsForPowers β heap OOB write past pcdacTable[128]
Verdict: REPRODUCED (harness, verbatim kernel code) β impact: dos (hardware-gated), twin of DF-4619 (ar5413)
Root cause
sys/dev/netif/ath/ath_hal/ar5212/ar2316.c β
ar2316getGainBoundariesAndPdadcsForPowers() writes the final PDADC table
through a uint32_t kk index into pPDADCValues in three unbounded loops:
- ar2316.c:454-458 β extrapolate-below loop (
pPDADCValues[kk++], iterations =-ss) - ar2316.c:464-465 β mid loop (
pPDADCValues[kk++] = VpdTable_I[ii][ss++], iterations =maxIndex) - ar2316.c:474-482 β extrapolate-above loop (
pPDADCValues[kk++], iterations =tgtIndex - maxIndex)
pPDADCValues is ahp->ah_pcdacTable (ar2316.c:512) = priv->pcdacTable,
a uint16_t[PWR_TABLE_SIZE_2316=128] member (ar2316.c:41,
PWR_TABLE_SIZE_2413=128 at ah_eeprom_v3.h:282) of the ath_hal_malloced
struct ar2316State (ar2316.c:733). No loop checks kk < 128; only the
tail backfill (ar2316.c:490-493) assumes kk <= 128.
Per-gain write count = max(0,-ss) + tgtIndex where
ss = pPdGainBoundaries[ii-1] - Pmin_t2[ii] - pdGainOverlap_t2 and
tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii].
pPdGainBoundaries is clamped to β€63 (ar2316.c:436-441) but
pdGainOverlap_t2 comes from the 4-bit AR_PHY_TPCRG5_PD_GAIN_OVERLAP
hardware field (ar2316.c:532-533; mask 0xF, ar5212phy.h:349-350) β 0..15,
and Pmin_t2 is EEPROM-derived. Summed over 4 gains the algebra allows
kk up to 3 + 63 + 6*overlap = 156 > 128 with realistic register values
(overlap 15), so up to 28 cells (56 bytes) past the array; the measured
walk with the crafted dataset below runs kk to 168 (40 cells / 80 bytes
past) into Bank1Data..Bank7Data (offsets verified: pcdacTable@72,
Bank1Data@328, struct size 2008) β an intra-object heap overflow of the
ath_hal_malloc allocation. The corrupted bank scratch is programmed to
the radio at the next ar2316SetRfRegs (ar2316.c:212-216).
Trigger (threat model)
Crafted EEPROM on an AR2316-bearing card (physical / supply-chain
attacker): xpd_mask=0xF (4 used pdGains), pwr_t4 curves giving the
{0,58,58,58} Pmin pattern (pwr_t4[0]=4pwr_I, +2delta per point β the
exact shape ar2413SetupRawDataset/ar2413EepromToRawDataset produce,
ah_eeprom_v3.c:633-728), TPCRG5 overlap set to 15. First channel set
after attach invokes ar2316SetPowerTable (ar2316.c:501) β the kk walk
(ar2316.c:535-537 call, no return-value check beyond the compiled-out
HALASSERT at :538).
How it was verified
Verbatim-extraction harness (ar2316_harness.c mode 1): the guest's
pristine /usr/src ar2316.c function bodies (lines 251-499) are
sed/awk-extracted at build time (see build.sh, anchor-based) and
compiled against a faithful struct ar2316State (member order from
ar2316.c:39-61, bank rows 1/1/1/29/3 counted from the shipped
ar5212.ini). HALASSERT compiled out to mirror the release build
(ah_internal.h:665-676). The bank region is pre-filled with 0xAAAA
canaries; mode 1 reports the first/last clobbered cell.
run.log (decisive lines):
mode=1 numPdGainsUsed(ret)=4 minPower=0x0000 DF-4648 REPRODUCED: pPDADCValues write walked past pcdacTable[127]: first clobbered u16 cell 0 bytes past array end, last 79 bytes past (value 0x007f) => kk ran to 168 => intra-object heap OOB WRITE past pcdacTable[PWR_TABLE_SIZE_2316=128]
Reproduced twice (run.log, run.2.log) with identical results.
Exploitation ceiling
Hardware-gated (needs attacker-supplied AR2316 card with crafted EEPROM).
The overflow stays inside the 2008-byte struct ar2316State allocation
(banks + vpd tables are the landing zone), values are constrained
(pdadc clamps 0/127, uint16 Vpd-derived). Realistic ceiling: corrupted
RF bank programming / radio malfunction / garbage tx power β a DoS-class
defect, not a uid0 route. No exfiltration channel.
Fix validation
fix.diff bounds all three write loops with kk < PWR_TABLE_SIZE_2316
(mirrors the accepted DF-4619 fix). fix_run.log (PATCHED mode 1):
DF-4648: NO overflow detected, bank canaries intact. Modes 2/3 still
reproduce on this diff (it only touches the kk loops) β those are
DF-4649/DF-4650, fixed by their own diffs; the combined patch leaves all
three modes clean.
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_2316; on the patched extraction mode 1 reports 'DF-4648: NO overflow detected' with the bank canary region intact (baseline reproduced 168-cell walk, 80 bytes past). Harness-level validation of the patched function bodies (re-extracted from a fix.diff-patched copy); kernel-build validation is hardware-gated (no AR2316 on the guest).
fix_run.log (PATCHED mode 1): 'DF-4648: NO overflow detected'
Confirmed kernel references
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:41
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:454
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:464
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:474
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:490
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:512
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:535
- sys/dev/netif/ath/ath_hal/ar5212/ar2316.c:733
- sys/dev/netif/ath/ath_hal/ah_eeprom_v3.h:282
- sys/dev/netif/ath/ath_hal/ar5212/ar5212phy.h:349
Detail
Exploit chain
crafted EEPROM (xpd_mask=0xF, Pmin {0,58,58,58}, overlap 15) -> ath attach reads cal dataset -> first channel set invokes ar2316SetPowerTable:535 -> getGainBoundariesAndPdadcsForPowers kk walk exceeds 128 -> pPDADCValues[128..167] overwrite Bank1/2/3/6/7Data -> corrupted RF banks programmed to the radio at next ar2316SetRfRegs (ar2316.c:212-216) + stale pdadc tail. No uid0 route: hardware-gated, intra-object, clamped 16-bit values.
Evidence (decisive lines)
run.log mode 1: 'DF-4648 REPRODUCED: ... first clobbered u16 cell 0 bytes past array end, last 79 bytes past ... kk ran to 168'; run.2.log identical; fix_run.log PATCHED mode 1: 'DF-4648: NO overflow detected' with bank canary intact; build.sh shows the verbatim extraction from /usr/src.
PoC changes
harness written from scratch (no seed PoC), modeled on the DF-4619 ar5413 pack: anchor-based verbatim extraction of ar2316.c:251-499 at build time on the guest, faithful struct (bank rows 1/1/1/29/3 counted from the shipped ar5212.ini), HALASSERT compiled out to mirror release (AH_ASSERT undefined), dataset crafted per ar2413SetupRawDataset/ar2413EepromToRawDataset shapes; spans >=2 chosen to isolate DF-4648 from the independent DF-4650 wrap; overlap 15 justified from the 4-bit TPCRG5 mask (unlike a 2-bit assumption).
Verified recommended fix
Bound all three pPDADCValues write loops with kk < PWR_TABLE_SIZE_2316 (see fix.diff).
Verdict
REPRODUCED on the DragonFly guest by compiling the verbatim kernel function bodies (anchor-extracted ar2316.c:251-499 from /usr/src, 249 lines) against a faithful ar2316State layout (bank rows 1/1/1/29/3 from the shipped ar5212.ini; verified offsets pcdac@72 Bank1@328 vpdI@1492 sizeof 2008): with an EEPROM-shaped crafted dataset (xpd_mask=0xF, Pmin pattern {0,58,58,58}, TPCRG5 overlap 15 -- register-realistic, the field is 4 bits, ar5212phy.h:349) the unbounded kk walk at ar2316.c:454/:464/:474 wrote 40 uint16 cells (80 bytes) past pcdacTable[PWR_TABLE_SIZE_2316=128] into the Bank1Data..Bank7Data members of the heap-allocated ar2316State object (intra-object overflow; RF bank scratch corrupted, reprogrammed to the radio at ar2316.c:212-216). Exploitation is gated on attacker-supplied AR2316 hardware with crafted EEPROM (physical/supply-chain); realistic ceiling is radio misprogramming/DoS, not privilege escalation. Fix validated: with kk<PWR_TABLE_SIZE_2316 guards the overflow is gone (bank canary intact, fix_run.log).
No comments yet.