# 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]`:

* `ar5212SetupTxQueue` DATA path scans `ahp->ah_txq[q].tqi_type` for
  `q < pCap->halTotalQueues` (lines 160-162) → reads past `ah_txq[9]`;
* found "inactive" (`HAL_TX_QUEUE_INACTIVE == 0`, `ah.h:226` — adjacent heap
  zeroes read as inactive), it does `qi = &ahp->ah_txq[q]` and
  `OS_MEMZERO(qi, sizeof(HAL_TX_QUEUE_INFO))` (lines 177-183) → **OOB heap
  write** into `ah_intrTxqs`/`ah_decompMask[]`/beyond the allocation;
* BEACON/CAB/UAPSD paths compute `q = halTotalQueues-1/-2/-3` (lines 137/144/
  152) then read `ah_txq[q]` (line 153, 178) → up to index 30;
* `ar5212SetTxQueueProps:91`, `GetTxQueueProps:109`, `ReleaseTxQueue:237`,
  `ResetTxQueue:277` all gate on `q >= halTotalQueues` → the same unclamped
  cap authorizes `ah_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):

```diff
--- 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.
