ResetKeyCacheEntry MIC-clear path lacks runtime bounds check on entry+64 (HALASSERT inert in release)
Summary
ar5212ResetKeyCacheEntry (ar5212_keycache.c:86-93): computes micentry=entry+64 and writes MIC key registers AR_KEYTABLE_KEY0..3 when slot type==TKIP and IS_MIC_ENABLED(ah) (true by default ar5212_attach.c:260). Only bound is HALASSERT(micentry<halKeyCacheSize) at :89 but HALASSERT expands to NOTHING unless AH_ASSERT defined (ah_internal.h:675) which is LINT64-only not GENERIC. Sibling ar5212SetKeyCacheEntry:185 has genuine runtime if(entry+64>=halKeyCacheSize)return AH_FALSE; ResetKeyCacheEntry does not. Invariant normally holds (SetKeyCacheEntry prevents creating TKIP slot at E where E+64>=size) but reset trusts it without re-verifying. Trigger requires privileged HAL capability manipulation: disable MIC -> install high-index TKIP key (SetKeyCacheEntry check skipped) -> re-enable MIC -> reset key. Impact: OS_REG_WRITE zero to MMIO offsets past keycache aperture (0x8800+(E+64)*32 beyond entry 127). Best case ignored/unmapped; worst case corrupts adjacent MAC register or PCI bus fault -> panic (local DoS). No host-RAM corruption. AV:L/AC:H/PR:H, A:L. Fix: replace HALASSERT with runtime if(micentry>=halKeyCacheSize)return AH_FALSE mirroring SetKeyCacheEntry:185.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2073 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full narrative: source-trace, mechanism, fix, Phase 8 validation | 3.7 KB | β raw |
| README.md | readme | original reviewer README with theoretical trigger sequence | 1.1 KB | β raw |
| fix.diff | suggested-fix | git-apply-able: replace inert HALASSERT with runtime guard mirroring SetKeyCacheEntry:185 | 1.1 KB | view raw |
| build.sh | build-script | pushes fix.diff, documents validated build path | 947 B | view raw |
| run.sh | run-script | reports HW-gated source-only status; confirms no ath HW | 817 B | view raw |
| env.txt | environment | guest uname, cc, HW-gate check, source-trace citations | 1.9 KB | view raw |
| fix_build_combined.log | build-log | FULL combined (DF-2073+DF-2074) nativekernel build, rc=0 -Werror | 5.6 MB | β download |
DF-2073 PoC β ResetKeyCacheEntry MIC-clear lacks runtime bounds check
Status
Speculative β not directly reachable from unprivileged userspace. Requires privileged HAL capability manipulation (disable MIC, install high-index TKIP key, re-enable MIC, then reset key).
Preconditions
- AR5212-class ath(4) NIC present
- GENERIC kernel (no AH_ASSERT β HALASSERT is inert)
- Privileged access to HAL capability control (disable/re-enable MIC)
Trigger sequence
- Disable MIC capability (clear AR_STA_ID1_CRPT_MIC_ENABLE)
- Install TKIP key at high index E where E < halKeyCacheSize but E+64 >= size (SetKeyCacheEntry:185 check is skipped when MIC disabled)
- Re-enable MIC
- Delete/reset the key β ar5212ResetKeyCacheEntry executes OS_REG_WRITE to AR_KEYTABLE_KEY0(E+64) with E+64 >= size β MMIO write past keycache aperture
Expected output
Best case: write ignored/unmapped (no visible effect). Worst case: kernel panic from PCI bus fault or adjacent register corruption.
Fix
Replace HALASSERT with runtime if (micentry >= halKeyCacheSize) return AH_FALSE,
mirroring ar5212SetKeyCacheEntry:185.
DF-2073 β ar5212 ResetKeyCacheEntry MIC-clear path lacks runtime bounds check
Verdict
REPRODUCED (source-only confirmation; HW-gated β no AR5212 ath(4) NIC on guest).
Severity / impact
Low. Realistic ceiling: local DoS via MMIO write past the keycache aperture (0x8800+(E+64)*32, E up to 127 β 0x9FE0 vs aperture end 0x9800). Best case the write is ignored/unmapped; worst case it corrupts an adjacent MAC register or raises a PCI bus fault β kernel panic. No host-RAM corruption, no escalation primitive β pure DoS/hardening gap.
Mechanism (cited path confirmed line-by-line)
ar5212ResetKeyCacheEntry(ah, entry) (sys/dev/netif/ath/ath_hal/ar5212/ar5212_keycache.c:65-97):
1. Runtime-bounds-checks entry < halKeyCacheSize at :70 (good).
2. Reads keyType = OS_REG_READ(AR_KEYTABLE_TYPE(entry)) at :75.
3. If keyType == AR_KEYTABLE_TYPE_TKIP && IS_MIC_ENABLED(ah) (:86):
- micentry = entry + 64 (:87).
- HALASSERT(micentry < halKeyCacheSize) at :89 is the only guard.
- OS_REG_WRITE(AR_KEYTABLE_KEY0..3(micentry), 0) at :90-93.
The HALASSERT is INERT on GENERIC:
- sys/dev/netif/ath/ath_hal/ah_internal.h:665-676 β #ifdef AH_ASSERT
expands to the assert; #else (line 675) #define HALASSERT(_x) β NOTHING.
- options AH_ASSERT appears ONLY in sys/config/LINT64:1104, NOT in
sys/config/X86_64_GENERIC. So on the default kernel, the assert is gone.
The sibling ar5212SetKeyCacheEntry (ar5212_keycache.c:153-190) DOES carry a
real runtime guard at :185 β if (IS_MIC_ENABLED(ah) && entry+64 >=
pCap->halKeyCacheSize) return AH_FALSE; β but it is gated on
IS_MIC_ENABLED(ah). So the invariant "no TKIP slot at E where E+64>=size"
only holds while MIC is enabled. ar5212ResetKeyCacheEntry trusts the invariant
without re-verifying, and the trust is backed only by the inert HALASSERT.
Trigger (theoretical; requires privileged HAL capability control)
- Disable MIC capability (clear
AR_STA_ID1_CRPT_MIC_ENABLE). - Install a TKIP key at high index E with E < halKeyCacheSize but E+64 >= size (SetKeyCacheEntry:185 check is skipped because MIC is disabled).
- Re-enable MIC.
- Delete/reset the key β
ar5212ResetKeyCacheEntryruns the MIC-clear branch βOS_REG_WRITEtoAR_KEYTABLE_KEY0(E+64)whereE+64 >= 128β MMIO offset 0x9FE0+ past the 0x9800 aperture (ar5212reg.h:334:AR_KEYTABLE(_n) = 0x8800 + _n*32).
Not reachable from unprivileged userspace β requires privileged HAL capability
manipulation AND an AR5212-class NIC. This guest has no such NIC
(pciconf -l | grep ath β no match), so runtime confirmation is impossible.
Fix
Replace the inert HALASSERT with a runtime guard mirroring
ar5212SetKeyCacheEntry:185, returning AH_FALSE (the existing error path)
when micentry >= halKeyCacheSize. See fix.diff.
The fix matches the finding markdown's ## Recommended fix proposal verbatim
(replace HALASSERT with if (micentry >= halKeyCacheSize) return AH_FALSE).
Fix validation (Phase 8)
fix.diffapplied to/usr/src(combined with DF-2074's fix).make -j6 nativekernel KERNCONF=X86_64_GENERICβ rc=0, -Werror clean (no errors, no warnings).kernel.stripped(15.7 MB) +kernel.debug(119 MB) produced.- Installed to
/boot/kernel/kernel; rebooted βkern.versionbumped#0β#1(Sat Jul 25 11:53:22 UTC 2026); guest boots healthy, no regression. - Runtime before/after NOT possible (HW absent) β
fix_status: not_testableat runtime, but build-validated + source-traced to close the path.
PoC changes
No source PoC to build/run (HW-gated). The README documents the theoretical
trigger. Evidence pack carries the source-trace citations, the fix.diff, the
combined build log, and the environment record.
Fix verification
not_testableVALIDATED build+boot. HW-gated.
NK_DONE rc=0; kernel #1 boots clean.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- a
- t
- h
- /
- a
- t
- h
- _
- h
- a
- l
- /
- a
- r
- 5
- 2
- 1
- 2
- /
- a
- r
- 5
- 2
- 1
- 2
- _
- k
- e
- y
- c
- a
- c
- h
- e
- .
- c
- :
- 8
- 6
- s
- y
- s
- /
- d
- e
- v
- /
- n
- e
- t
- i
- f
- /
- a
- t
- h
- /
- a
- t
- h
- _
- h
- a
- l
- /
- a
- h
- _
- i
- n
- t
- e
- r
- n
- a
- l
- .
- h
- :
- 6
- 7
- 5
Detail
Exploit chain
none (DoS/hardening).
Evidence (decisive lines)
HW-GATED (no AR5212 NIC). Source-CONFIRMED. ar5212ResetKeyCacheEntry HALASSERT(micentry<halKeyCacheSize) is inert on GENERIC (AH_ASSERT LINT64-only). Sibling guard at :185 is MIC-gated. MMIO write pas
Verified recommended fix
Replace inert HALASSERT with runtime guard returning AH_FALSE, mirroring SetKeyCacheEntry:185.
Verdict
HW-GATED (no AR5212 NIC). Source-CONFIRMED. ar5212ResetKeyCacheEntry HALASSERT(micentry<halKeyCacheSize) is inert on GENERIC (AH_ASSERT LINT64-only). Sibling guard at :185 is MIC-gated. MMIO write past keycache aperture.
No comments yet.