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

ath_hal_9287EepromAttach error paths leak the malloc'd HAL_EEPROM_9287 (~1KB permanently per failed attach): three post-malloc returns never free or install it, and ar9287Attach's bad: unwind is a no-op for it

Summary

Three error returns between the malloc and the ownership install (read failure, bad checksum, bad version) orphan the ~1KB block; the caller's detach can't free memory it was never handed (eepromDetach is a no-op until installed). EEPROM content trivially triggers it - hotplug-repeatable unbounded kernel heap exhaustion under the untrusted-EEPROM model. v14/v4k twins carry the same family bug. fail-label+free fix. [PASS-2 ATTRIBUTION: the leak code lives in ah_eeprom_9287.c:322/335/376/395 (malloc + 3 non-freeing error returns) - apply the fail-label fix there; this row covers the same defect as seen from the caller]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4873 Β· 4 files
FileTypeDescriptionSize
VERDICT.md file 3.9 KB ↓ raw
env.txt file 272 B view raw
fix.diff file 1.2 KB view raw
verdict.json file 3.7 KB view raw
VERDICT.md file
↓ download raw

DF-4873 β€” ath_hal_9287EepromAttach error paths leak the malloc'd HAL_EEPROM_9287 (~1KB per failed attach; ar9287Attach's bad: path cannot reclaim it)

What was verified (static, line-by-line)

  1. The allocation. sys/dev/netif/ath/ath_hal/ah_eeprom_9287.c:322-326 c ee = ath_hal_malloc(sizeof(HAL_EEPROM_9287)); if (ee == AH_NULL) return HAL_ENOMEM; ath_hal_malloc = kmalloc(size, M_ATH_HAL, M_INTWAIT | M_ZERO) (ah_osdep.c:121-124). sizeof(HAL_EEPROM_9287) β‰ˆ 1 KB (ee_base ~0x3d0 bytes + ee_rdEdgesPower[8*12] = 96 Γ— sizeof(RD_EDGES_POWER)).

  2. Three error returns after the allocation, none free it. - mid-image read failure β†’ return HAL_EEREAD; (ah_eeprom_9287.c:331-336) - bad checksum β†’ return HAL_EEBADSUM; (ah_eeprom_9287.c:373-377) - bad EEPROM version β†’ return HAL_EEBADSUM; (ah_eeprom_9287.c:392-396)

  3. Ownership is never transferred on those paths. AH_PRIVATE(ah)->ah_eeprom and the ah_eepromDetach/ah_eepromGet/... ops are installed only at the very end (ah_eeprom_9287.c:400-406), after all of the error returns.

  4. The caller's unwind cannot reclaim it. ar9287Attach (sys/dev/netif/ath/ath_hal/ar9002/ar9287_attach.c:239-241): c ecode = ath_hal_9287EepromAttach(ah); if (ecode != HAL_OK) goto bad; /* ar9287_attach.c:362-367 */ bad: if (ah != AH_NULL) ah->ah_detach(ah); /* == ar5416Detach (ar5416_attach.c:97) */ ar5416Detach (ar5416_attach.c:490-509) calls ath_hal_eepromDetach(ah) at line 507, which is the macro c #define ath_hal_eepromDetach(_ah) do { \ if (AH_PRIVATE(_ah)->ah_eepromDetach != AH_NULL) \ AH_PRIVATE(_ah)->ah_eepromDetach(_ah); \ } while (0) /* ah_internal.h:474-477 */ β€” a no-op because ah_eepromDetach was never installed β€” then ath_hal_free(ah) frees only the struct ath_hal_9287, not ee. The ~1 KB HAL_EEPROM_9287 is orphaned. Leak confirmed.

  5. Family bug. v14EepromAttach has the identical shape (ah_eeprom_v14.c:363 malloc; return HAL_EEREAD at ~:375 with no free); v4k mirrors it. This pack documents the ar9287 path (the audited file's call site); the v14/v4k twins should get the same fail: label.

Trigger / threat model

Untrusted-EEPROM / hostile-hardware (physical access or resold/reprogrammed card): any of the three failures is trivially produced by EEPROM content (checksum mismatch being the easiest β€” the attacker recomputes or corrupts it). Each attach attempt with such a card leaks ~1 KB permanently (M_ATH_HAL kernel heap, no reference, never freed). Hotplug insert/remove cycles repeat the leak without bound. No UAF, no corruption β€” a slow, permanent kernel memory-exhaustion hygiene bug. Severity Low.

Why no runtime PoC (untested, family precedent DF-4793/DF-4799/DF-4548)

The leak requires an AR9287 card whose EEPROM reads fail partway or fails checksum/version β€” the QEMU audit guest has no Atheros NIC, so ar9287Attach is never reached. The static ownership trace above is complete and has no gaps (allocation β†’ error return β†’ caller unwind β†’ detach macro β†’ nothing frees ee).

Fix

fail: label freeing ee before each post-malloc error return β€” see fix.diff (git-apply-able, verified with git apply --check). Not guest-build-validated (no reason to build a kernel for a change whose trigger hardware is absent; change is mechanical).

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

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored from line-accurate root cause and verified git-apply-able; runtime fix validation impossible on the guest (no AR9287 hardware to trigger a failed eeprom attach)

findings/poc/DF-4873/fix.diff
↓ fix.diffper-fix-DF-4873

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4873/VERDICT.md (full ownership trace with path:line); findings/poc/DF-4873/fix.diff (git apply --check clean)

PoC changes

No runtime PoC possible: requires an AR9287 card whose EEPROM fails mid-read/checksum/version; no Atheros NIC on the QEMU guest. Verification is the complete static ownership trace in VERDICT.md

Verified recommended fix

Add a fail: label to ath_hal_9287EepromAttach that ath_hal_free()s the not-yet-installed ee, and route the three post-malloc error returns through it (see fix.diff)

Verdict

Kernel heap memory leak (permanent, ~1KB per failed attach) on the ar9287 EEPROM-attach error paths: ath_hal_9287EepromAttach mallocs HAL_EEPROM_9287 at ah_eeprom_9287.c:322 (ath_hal_malloc -> kmalloc M_ATH_HAL M_ZERO, ah_osdep.c:121-124), then returns HAL_EEREAD at :335 (mid-image read failure), HAL_EEBADSUM at :376 (bad checksum) or :395 (bad EEPROM version) without freeing ee and without installing it - AH_PRIVATE(ah)->ah_eeprom and the ah_eepromDetach op are set only at :400-406, after every error return. The caller's unwind (ar9287_attach.c:239-241 goto bad -> ah->ah_detach == ar5416Detach, ar5416_attach.c:490-509) calls ath_hal_eepromDetach at :507, which is a no-op when ah_eepromDetach was never installed (ah_internal.h:474-477), then ath_hal_free frees only the ath_hal itself. The HAL_EEPROM_9287 is orphaned with no back-reference. Attacker model (untrusted EEPROM / hostile card, physical access or hotplug): a corrupt or reprogrammed EEPROM trivially hits checksum/version failure; every attach attempt leaks ~1KB; hotplug cycles repeat it without bound (slow kernel memory exhaustion). Same shape as DF-4209 (legacy eeprom leak, Info) but attacker-chosen trigger content and hotplug-repeatable. Statically certain: allocation -> error return -> caller unwind -> detach macro no-op, no gap. No AR9287 NIC on the QEMU guest so no runtime PoC (family precedent DF-4793/DF-4799/DF-4548 untested). v14EepromAttach (ah_eeprom_v14.c:363+) and v4k have the identical family bug - fix them the same way.