β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4680

Unbounded kk pdadc walk writes past pcdacTable[PWR_TABLE_SIZE_2317=128] β€” 80-byte intra-object heap OOB write into RF bank scratch (twin of DF-4619/4648/4660)

Summary

The three pdadc write loops share kk with no bound; minimum-guaranteed walk for 4 gains is 156>128 (measured kk=168, 80 bytes into the RF bank scratch reprogrammed to the radio). VERBATIM-EXTRACTION harness on guest REPRODUCES with canary; fix validated -> no overflow. Cap-kk fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4680 Β· 13 files
FileTypeDescriptionSize
README.md β€” 1.6 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 β€” 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 β€” 916 B view raw
verdict.json β€” 4.1 KB view raw

DF-4680 β€” ar2317: unbounded kk pdadc walk writes past pcdacTable[128]

Twin of DF-4619 (ar5413) / DF-4648 (ar2316) / DF-4660 (ar2413), verified for sys/dev/netif/ath/ath_hal/ar5212/ar2317.c.

Build (on the DragonFly guest)

cd /root/df2317 && sh build.sh

build.sh copies the pristine /usr/src/.../ar2317.c (md5-verified equal to the audit tree), verbatim-extracts lines ~228-477 (GetLowerUpperIndex, ar2317FillVpdTable, interpolate_signed, ar2317getGainBoundariesAndPdadcsForPowers) by anchor, and compiles them against a faithful struct ar2317State (bank rows 1/1/1/30/3 from the shipped ar5212.ini).

Run

./ar2317_harness 1        # this finding
./ar2317_harness 2|3      # siblings DF-4681 / DF-4682 (same pack set)
./ar2317_harness 4|5      # DF-4683
sh run.sh                 # all five modes + uninit-variance repeats

Expected (baseline)

DF-4680 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

kk is bounded by nothing in the three pPDADCValues[kk++] loops (ar2317.c:434/:443/:457); with 4 used gains, 63-clamped gain boundaries and TPCRG5 overlap 15 the guaranteed minimum walk is 156 > 128, so the tail of the pdadc table spills 80 bytes into Bank1Data..Bank7Data (RF bank scratch, reprogrammed to the radio at ar2317.c:190-194).

Fix validation

sh fix_validate.sh        # applies fixA..fixD to copies, re-extracts, re-runs

fixA (this pack's fix.diff) caps kk at PWR_TABLE_SIZE_2317 in all three loops β†’ patched mode 1 prints DF-4680: NO overflow detected.

VERDICT.md
↓ download raw

DF-4680 β€” VERDICT

Status: REPRODUCED (guest, verbatim-extraction harness) Β· impact: dos Β· confidence: certain Β· fix: fixed (validated)

What was verified

The three pPDADCValues[kk++] loops in ar2317getGainBoundariesAndPdadcsForPowers() β€” extrapolate-below (ar2317.c:432-436), main walk (:442-443), extrapolate-above (:452-461) β€” share one running index kk into pPDADCValues with no bound. pPDADCValues is ahp->ah_pcdacTable (ar2317.c:490), aliased to priv->pcdacTable[PWR_TABLE_SIZE_2317=128] at attach (ar2317.c:729; PWR_TABLE_SIZE_2413 = 128, ah_eeprom_v3.h:282).

The harness compiles the guest's own function bodies (anchor-extracted from /usr/src, md5-verified) against the faithful struct (offsets printed in build.log: pcdacTable@72, Bank1Data@328, vpdTable_L@472, sizeof=2008) and drives them with an EEPROM-shaped dataset identical in shape to what the shared SetupRawDataset produces (xpd_mask=0xF, four gains with numVpd=4, Pmin pattern {0,58,58,58}, TPCRG5 overlap 15 β€” the mask field is 4 bits, ar5212phy.h:349).

Observed (run.log, mode 1): kk ran to 168; cells 128..167 (80 bytes) landed in Bank1Data..Bank7Data (0xAAAAAAAA canary destroyed, values 0x0000..0x007F). The overwritten banks are written to the radio by ar2317SetRfRegs (ar2317.c:190-194) β€” radio misprogramming at best, crash at worst. The overflow is intra-object (stays inside the 2008-byte ath_hal_malloc allocation) and written values are clamped (0/127), so there is no uid0 route; severity Low, consistent with the ar5413/ar2316/ar2413 twins.

Why the bound cannot hold on legit-looking data

Worst-case per-gain walk = extrapolate-below (ss from boundary[ii-1]-Pmin[ii]-overlap, can be β‰ˆ -(63+15)) + main (maxIndex ≀ 63) + extrapolate-above (up to 63+15-0). With the minimum-guaranteed sum 3+63+6Γ—15 = 156 > 128 for the last gain alone, and up to 4 gains contributing, the overflow needs no malformed EEPROM beyond ordinary-looking cal data.

Fix validation

fix.diff = kk < PWR_TABLE_SIZE_2317 guards on all three loops. fix_validate.sh applies it to a copy of the pristine guest source, re-extracts verbatim, rebuilds, re-runs: patched mode 1 β†’ DF-4680: NO overflow detected (canary intact). git apply --check clean against sys/; never applied to sys/.

Not re-reported

DF-1806 (pass 1, numChannels==0 β†’ idxL/idxR wild reads) β€” different trigger, different guard. Siblings DF-4681 / DF-4682 / DF-4683 cover the other defects in the same function.

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

fixed
baseline reproduced→ patch + rebuild →patched clean

Validated on the guest: fix.diff (kk < PWR_TABLE_SIZE_2317 guards on all three pPDADCValues[kk++] loops) applied to a copy of the pristine guest ar2317.c, function bodies re-extracted verbatim from the patched copy, harness rebuilt and re-run. Baseline reproduces the overflow; patched build shows 'DF-4680: NO overflow detected' (bank canary intact). fix.diff is git-apply-able against the pristine tree (git apply --check clean) and was never applied to sys/.

fix_run.log ('===== fixA' section: PATCHES_APPLIED, FIXED_BUILD_OK, 'NO overflow detected') vs run.log mode 1; fix.diff in pack.
↓ fix.diffper-fix-DF-4680

Confirmed kernel references

Detail

Exploit chain

crafted v3 EEPROM on AR2317 card -> ath attach reads ee_rawDataset2413 -> ar5212SetTransmitPower (ar5212_reset.c:2006-2036) -> ar2317SetPowerTable:513-515 -> ar2317getGainBoundariesAndPdadcsForPowers unbounded kk walk -> 80-byte intra-object heap OOB write past pcdacTable[128] into RF bank scratch -> banks programmed to radio (ar2317.c:190-194): radio misprogramming / crash. No uid0 route (values clamped 0..127, intra-object only).

Evidence (decisive lines)

run.log mode 1: 'DF-4680 REPRODUCED ... kk ran to 168'; build.log struct offsets (pcdac@72 Bank1@328 vpdTable_L@472 sizeof 2008); fix_run.log patched mode 1: 'NO overflow detected'; fix.diff (git-apply verified vs sys/).

PoC changes

Harness ported from the DF-4660 (ar2413) twin: bank row counts 1/1/1/30/3 (ar5212.ini ar5212Bank*_2317), ar2317 names/anchors, 72-cell bank canary window, ar2317.c:35-37 typedef aliases added ahead of the extraction, object placed at a page end with a guard page (shared with modes 4/5 of DF-4683). No kernel-code changes -- function bodies compiled verbatim.

Verified recommended fix

Add kk < PWR_TABLE_SIZE_2317 guards to all three pPDADCValues[kk++] loops in ar2317getGainBoundariesAndPdadcsForPowers (fix.diff).

Verdict

REPRODUCED on the DragonFly guest (verbatim-extraction harness, mode 1): 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 ar2317.c:434/:443/:457 wrote 40 uint16 cells (80 bytes) past pcdacTable[PWR_TABLE_SIZE_2317=128] into the Bank1Data..Bank7Data members of the heap-allocated ar2317State object (intra-object overflow; RF bank scratch corrupted, reprogrammed to the radio at ar2317.c:190-194). kk ran to 168; with 63-clamped boundaries and overlap 15 the minimum-guaranteed walk is 3+63+6*15=156 > 128. Exploitation is gated on attacker-supplied AR2317 hardware with crafted EEPROM (physical/supply-chain); realistic ceiling is radio misprogramming/DoS, not privilege escalation. Twin of DF-4619 (ar5413) / DF-4648 (ar2316) / DF-4660 (ar2413). Fix validated: with kk<PWR_TABLE_SIZE_2317 guards the overflow is gone (bank canary intact, fix_run.log).