DF-4681 / verdict.json
{ "finding_id": "DF-4681", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 3, "guest_dirty": 0, "build_cmd": "cd /root/df2317 && sh build.sh", "run_cmd": "./ar2317_harness 2", "code_hash": "2a6f1714d2a77176c8c164e7a8ea4dd9153f22a3bdecb466c9af8ded31a0d901", "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 functions 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.", "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": "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).", "kernel_refs": [ "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" ], "poc_changes": "Same harness as DF-4680 (mode 2); no kernel-code changes.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Validated 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_evidence": "fix_run.log (===== fixB section: PATCHES_APPLIED, FIXED_BUILD_OK, ret=0/sentinel/no propagation) vs run.log mode 2; fix.diff in pack.", "attempts": 1, "notes": "Distinct root cause and guard from known DF-1806 (numChannels==0 -> idxL/idxR wild reads, pass 1, NOT re-reported): DF-1806 fires with numChannels==0 regardless of gain data; this fires with numChannels>=1 and xpd_mask==0. The same uint32-underflow class also occurs at ar2317.c:445-446 on a different trigger (flat curves) -- filed as DF-4682.", "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)." } |