ar5212_xmit: halTotalQueues taken unclamped from EEPROM AR_EEP_MAXQCU (raw 5-bit, 1..31) bounds ah_txq[HAL_NUM_TX_QUEUES=10] in every queue op -> SetupTxQueue OOB scan read + OS_MEMZERO OOB heap write at attach on crafted EEPROM
Summary
ah_txq is sized 10 but every queue op bounds by halTotalQueues, copied unclamped from the raw 5-bit EEPROM MAXQCU (1..31). SetupTxQueue's DATA path scans past ah_txq[9], finds a zeroed adjacent slot (INACTIVE==0), then OS_MEMZEROs sizeof(HAL_TX_QUEUE_INFO) out of bounds; BEACON/CAB/UAPSD reach index 30. Requires a crafted EEPROM (physical-card tampering model). AH_MIN clamp fix build-validated. CROSS-FILE: ar5416_attach.c:957-959 has the identical unclamped assignment - flagged for the 11n HAL.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4520 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full dataflow trace + fix rationale | 3.4 KB | β raw |
| verdict.json | verdict-machine | persist_poc.py input | 3.3 KB | view raw |
| fix.diff | suggested-fix | AH_MIN(val, HAL_NUM_TX_QUEUES) clamp in ar5212_attach.c | 372 B | view raw |
| build.log | build-log | full nativekernel log, BUILD_RC=0, -Werror (shared with DF-4519 build) | 5.1 MB | β download |
| build.sh | script | build validation steps | 147 B | view raw |
| run.sh | script | no-op: HW-gated, see VERDICT.md | 156 B | view raw |
DF-4520 β ar5212_xmit.c: EEPROM-derived halTotalQueues (MAXQCU, 5 bits, unclamped) bounds ah_txq[10] β OOB read + OS_MEMZERO OOB write
What
struct ath_hal_5212 sizes its queue array ah_txq[HAL_NUM_TX_QUEUES] with
HAL_NUM_TX_QUEUES == 10 (sys/dev/netif/ath/ath_hal/ar5212/ar5212.h:271,
sys/dev/netif/ath/ath_hal/ah.h:236). At attach,
ar5212Attach copies the EEPROM opcap field AR_EEP_MAXQCU into
pCap->halTotalQueues without clamping
(sys/dev/netif/ath/ath_hal/ar5212/ar5212_attach.c:862-865;
ah_eeprom_v3.c:1640-1644 returns MS(ee->ee_opCap, AR_EEPROM_EEPCAP_MAXQCU)
β a raw 5-bit field, 1..31, ah_eeprom_v3.h:72-73).
Every queue function in ar5212_xmit.c then uses halTotalQueues (not the
array bound) as the limit for ah_txq[q]:
ar5212SetupTxQueueDATA path scansahp->ah_txq[q].tqi_typeforq < pCap->halTotalQueues(lines 160-162) β reads pastah_txq[9];- found "inactive" (
HAL_TX_QUEUE_INACTIVE == 0,ah.h:226β adjacent heap zeroes read as inactive), it doesqi = &ahp->ah_txq[q]andOS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO))(lines 177-183) β OOB heap write intoah_intrTxqs/ah_decompMask[]/beyond the allocation; - BEACON/CAB/UAPSD paths compute
q = halTotalQueues-1/-2/-3(lines 137/144/ 152) then readah_txq[q](line 153, 178) β up to index 30; ar5212SetTxQueueProps:91,GetTxQueueProps:109,ReleaseTxQueue:237,ResetTxQueue:277all gate onq >= halTotalQueuesβ the same unclamped cap authorizesah_txq[10..30]accesses.
Trigger (honest)
Requires an ar5212-family NIC whose EEPROM opcap encodes MAXQCU > 10 (real
AR5212 parts program β€10; the 5-bit field allows up to 31) β i.e. a crafted /
corrupted EEPROM (physical-card trust boundary), read at driver attach
(if_ath β ar5212GetCapability/queue setup). No unprivileged-software
trigger exists: the value comes only from the card's own EEPROM, and a card
whose EEPROM an attacker can rewrite implies physical access (out of the
local-user threat model; the same device could DMA memory anyway).
Low / speculative β latent hardening gap of the same family as DF-2201 /
DF-2073.
Fix
Clamp the cap where it enters (single choke point):
--- a/sys/dev/netif/ath/ath_hal/ar5212/ar5212_attach.c
+++ b/sys/dev/netif/ath/ath_hal/ar5212/ar5212_attach.c
@@ -860,7 +860,7 @@
pCap->halTsfAddSupport = AH_TRUE;
if (ath_hal_eepromGet(ah, AR_EEP_MAXQCU, &val) == HAL_OK)
- pCap->halTotalQueues = val;
+ pCap->halTotalQueues = AH_MIN(val, HAL_NUM_TX_QUEUES);
else
pCap->halTotalQueues = HAL_NUM_TX_QUEUES;
Full diff in fix.diff. Note: ar5416_attach.c:957-959 has the identical
unclamped AR_EEP_MAXQCU assignment for the 11n HAL (out of this file's
scope; should get the same one-line clamp when audited).
Build validation
Applied to guest /usr/src together with DF-4519's diff; full
make nativekernel KERNCONF=X86_64_GENERIC rc=0 with -Werror
(ar5212_attach.o rebuilt: build.log lines 7145/13441). Runtime behavior is
not testable on the audit guest (no Atheros NIC).
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
fixedbuild-only validation (runtime HW-gated): fix.diff applied to guest /usr/src; make nativekernel KERNCONF=X86_64_GENERIC rc=0 with -Werror; ar5212_attach.o rebuilt (build.log:7145,13441,14895-14897). Baseline/patched runtime not reproducible without an Atheros NIC with a crafted EEPROM.
findings/poc/DF-4520/build.log (full untrimmed nativekernel log, BUILD_RC=0; decisive lines 3, 7145, 13441, 14895, 14897)
Confirmed kernel references
- sys/dev/netif/ath/ath_hal/ar5212/ar5212_xmit.c:137
- sys/dev/netif/ath/ath_hal/ar5212/ar5212_xmit.c:144
- sys/dev/netif/ath/ath_hal/ar5212/ar5212_xmit.c:152
- sys/dev/netif/ath/ath_hal/ar5212/ar5212_xmit.c:160
- sys/dev/netif/ath/ath_hal/ar5212/ar5212_xmit.c:177
- sys/dev/netif/ath/ath_hal/ar5212/ar5212_xmit.c:183
- sys/dev/netif/ath/ath_hal/ar5212/ar5212_attach.c:862
- sys/dev/netif/ath/ath_hal/ar5212/ar5212.h:271
- sys/dev/netif/ath/ath_hal/ah_eeprom_v3.c:1640
- sys/dev/netif/ath/ath_hal/ah_eeprom_v3.h:72
Detail
Exploit chain
[]
Evidence (decisive lines)
AR_EEP_MAXQCU returns MS(ee->ee_opCap, 0x01F0) = raw 5-bit 1..31 (ah_eeprom_v3.c:1640-1644, ah_eeprom_v3.h:72-73); ar5212_attach.c:862-865 assigns it to halTotalQueues unclamped; ah_txq is [HAL_NUM_TX_QUEUES=10] (ar5212.h:271, ah.h:236); ar5212_xmit.c:160-162 scans ah_txq[q] q<halTotalQueues, :177-183 OS_MEMZEROs &ah_txq[q] (HAL_TX_QUEUE_INACTIVE==0 so adjacent zeroed heap reads as free), :137/:144/:152 compute q=halTotalQueues-1/-2/-3. Fix clamp AH_MIN(val, HAL_NUM_TX_QUEUES) build-validated rc=0 -Werror (build.log:7145,13441,14897).
PoC changes
n/a (no runtime PoC attempted; source-only confirmation per HW-gated protocol)
Verified recommended fix
Clamp pCap->halTotalQueues = AH_MIN(val, HAL_NUM_TX_QUEUES) at the single choke point in ar5212_attach.c (and mirror in ar5416_attach.c).
Verdict
untested (runtime): halTotalQueues is a raw 5-bit EEPROM opcap field copied unclamped into the bound used for ah_txq[10] everywhere in ar5212_xmit.c; a crafted EEPROM (MAXQCU 11..31) makes ar5212SetupTxQueue read ah_txq[10..30] and OS_MEMZERO an out-of-bounds slot at attach. Real cards program <=10, so the trigger needs physical EEPROM tampering; no Atheros NIC exists on the guest. Source-confirmed; fix.diff clamps the cap and build-validated rc=0 -Werror.
No comments yet.