ath_hal_v4kEepromAttach leaks the malloc'd HAL_EEPROM_v4k (1152 B, M_ATH_HAL) on all three post-malloc error returns β third and final member of the eeprom-attach leak family (DF-4873 ar9287 / DF-4913 v14 / DF-1995 ar5210), verified live
Summary
Malloc at :311, three non-freeing error returns (read/checksum/version) before the install at :390-396 - the caller's detach-based unwind is a no-op. KLD harness drove the REAL in-kernel symbol: 3x200 failed attaches -> 600 blocks/675K PERMANENT across kldunload (stacking to 1.32M on round 2); the valid control perfectly balanced. Family COMPLETE with this filing. 3x ath_hal_free fix (git-apply clean).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4956 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| v4kleak.c | β | 6.1 KB | view raw | |
| Makefile | β | 193 B | β download | |
| build.sh | β | 150 B | view raw | |
| run.sh | β | 144 B | view raw | |
| build.log | β | 6.0 KB | view raw | |
| run.log | β | 414 B | view raw | |
| run.2.log | β | 164 B | view raw | |
| console.log | β | 1.0 KB | view raw | |
| env.txt | β | 264 B | view raw | |
| VERDICT.md | β | 6.0 KB | β raw | |
| verdict.json | β | 4.6 KB | view raw | |
| fix.diff | β | 664 B | view raw | |
| README.md | β | 2.8 KB | β raw |
DF-4956 β ath_hal_v4kEepromAttach error-path leak (eeprom-attach family, 3rd twin)
What
ath_hal_v4kEepromAttach() (sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:282-399)
mallocs HAL_EEPROM_v4k (1152 bytes, M_ATH_HAL, zeroed β ah_osdep.c:121-124)
at :311 and has three post-malloc error returns that neither free the object
nor install it:
- :324
return HAL_EEREAD;β mid-image EEPROM read failure - :366
return HAL_EEBADSUM;β XOR checksum != 0xffff - :385
return HAL_EEBADSUM;β EEPROM version nibble != AR5416_EEP_VER (14)
The eeprom only becomes reachable at :390-396
(AH_PRIVATE(ah)->ah_eeprom = ee, ah_eepromDetach = v4kEepromDetach), after
every error return. The caller (ar9285_attach.c:246-248 goto bad β
ah->ah_detach β ar5416Detach (ar5416/ar5416_attach.c:490-508) β
ath_hal_eepromDetach macro (ah_internal.h:474-477)) is a no-op when
ah_eepromDetach was never installed β the 1152-byte block is permanently
orphaned.
Third and final member of the family: DF-4873 (ar9287, ~1KB), DF-4913 (v14, 4.8KB, verified live), DF-1995 (ar5210 v1). Both prior verdicts predicted this file carries the same bug.
Build (on the DragonFly guest, as root, in this directory)
./build.sh # make - builds v4kleak.ko KLD against the running kernel
Requires the kernel source tree at /usr/src and built kernel headers at
/usr/obj/usr/src/sys/X86_64_GENERIC (both present on the audit guest).
ath_hal_v4kEepromAttach is compiled into the guest kernel
(nm /boot/kernel/kernel β ffffffff8034f1a0 T ath_hal_v4kEepromAttach).
Run (as root)
./run.sh
Expected output
--- vmstat -m ath_hal BEFORE ---
(no ath_hal line β 0 in-use blocks)
...
ath_hal 600 675K 0 390M 800 <- after trigger
ath_hal 600 675K 0 390M 800 <- after kldunload (unchanged: permanent)
Serial console (vm.sh log):
DF-4956 PoC loaded (inert): sizeof(HAL_EEPROM_v4k)=1152 sizeof(ar5416eeprom_4k)=376 NWORDS=188 owl_eep_start_loc=64 DF-4956[EEREAD@324]: 200 attaches, 0 HAL_OK, 200 with ah_eeprom==NULL (orphaned mallocs) DF-4956[EEBADSUM@366]: 200 attaches, 0 HAL_OK, 200 with ah_eeprom==NULL (orphaned mallocs) DF-4956[BADVER@385]: 200 attaches, 0 HAL_OK, 200 with ah_eeprom==NULL (orphaned mallocs) DF-4956[control]: 200 attaches, 200 HAL_OK, each detached (balanced; expect no ath_hal growth)
Second trigger round stacks: 600 blocks/675K β 1.17K blocks/1.32M (run.2.log). Success criterion: β₯1 failed attach mode orphans its block permanently (vmstat ath_hal in-use grows by exactly NFAIL per mode and survives kldunload), while the valid-image control leaves in-use unchanged.
Fix
fix.diff: ath_hal_free(ee) before each of the three error returns β
identical to the DF-4873/DF-4913 family fix.
DF-4956 VERDICT β REPRODUCED (kernel memory leak)
Finding
ath_hal_v4kEepromAttach() (sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:282-399)
mallocs HAL_EEPROM_v4k at :311 (ath_hal_malloc β kmalloc(M_ATH_HAL,
M_INTWAIT|M_ZERO), ah_osdep.c:118-124; 1152 bytes on this build) and has
three post-malloc error returns that neither free the object nor install it:
- :324
return HAL_EEREAD;β mid-image EEPROM read failure (loop at :318-326) - :366
return HAL_EEBADSUM;β XOR checksum != 0xffff (check at :363) - :385
return HAL_EEBADSUM;β EEPROM version nibble != AR5416_EEP_VER (14)
The eeprom becomes reachable only at :390-396
(AH_PRIVATE(ah)->ah_eeprom = ee, ah_eepromDetach = v4kEepromDetach), after
every error return. The caller (ar9285_attach.c:246-248 ecode != HAL_OK β
goto bad β ah->ah_detach(ah) at :393 β ar5416Detach
(ar5416/ar5416_attach.c:490-508) β ath_hal_eepromDetach macro
(ah_internal.h:474-477)) is a no-op when ah_eepromDetach was never
installed β the 1152-byte block is permanently orphaned. This is the third and
final member of the eeprom-attach leak family: DF-4873 (ar9287, ~1KB per
block), DF-4913 (v14, 4800B, verified live 600 blocks/2.93MB), DF-1995
(ar5210 v1). Both prior verdicts predicted this file has the identical bug β
it does.
Reproduction (2026-09-25, guest DragonFly 6.5-DEVELOPMENT #0 X86_64_GENERIC)
KLD PoC v4kleak.c calls the real in-kernel ath_hal_v4kEepromAttach()
(ath_hal compiled into the guest kernel; symbol verified via
nm /boot/kernel/kernel β ffffffff8034f1a0 T ath_hal_v4kEepromAttach) with
a synthetic zeroed struct ath_hal_private whose ah_eepromRead callback
serves crafted v4k-variant EEPROM images (image starts at word offset
owl_eep_start_loc == 64, per ah_eeprom_v4k.h:34-39):
| mode | trigger | return | orphans |
|---|---|---|---|
| EEREAD@324 | read callback fails at image word 5 | HAL_EEREAD | 200/200 |
| EEBADSUM@366 | XOR checksum != 0xffff | HAL_EEBADSUM | 200/200 |
| BADVER@385 | checksum-valid, version nibble 0 | HAL_EEBADSUM | 200/200 |
| control | fully valid image (version 0xE001) | HAL_OK | 0 (detach frees) |
Result (vmstat -m, malloc type ath_hal):
- baseline: 0 blocks β 600 blocks / 675K after trigger; unchanged after
kldunload(permanent β no back-reference to the orphaned block exists). - run 2 (run.2.log): 600 β 1.17K blocks / 1.32M (leak accumulates per attach round; 1152 B Γ 1200 β 1.32 MB β).
- control contributed 200 transient allocations that were all freed via the
installed
v4kEepromDetach(ah_eeprom_v4k.c:267-274) β in-use unchanged by control, proving the mallocβdetach pairing is balanced and only the three error paths orphan.
See run.log / run.2.log / console.log in this directory.
Impact ceiling
Unbounded kernel-heap exhaustion: 1152 B per failed attach. Trigger content is attacker-chosen on the untrusted-EEPROM model (corrupt/reprogrammed EEPROM or hostile ar9285 "Kite" card); every attach attempt fails the same way. Repeatable without reboot via hotplug / devd re-probe / driver reload cycles β same model as DF-4873/DF-4913 (physical access or crafted hardware). Not remotely triggerable, not a memory-safety corruption: impact = kernel memory leak (DoS by exhaustion), Low severity.
Why it is not worse
The orphaned block is zeroed at malloc (M_ZERO) and never handed to any consumer after the error return β no UAF, no info leak (block never copied out; the diag export path v4kEepromDiag at :131-144 only runs after a successful install), no overflow. Pure memory loss.
Pass-2 negatives worth recording (v4k file beyond the family leak)
- read loop :318-326 writes exactly
sizeof(struct ar5416eeprom_4k)= 376 B (188 words) into the first member of the 1152-B object β compile-time bound, not attacker-enlargable. No OOB. v4kEepromReadCTLInfo:232-262: writes β€ 12Γ8 entries intoee_rdEdgesPower[96](max index 91);ctlIndex[12]/ctlData[12]indexed only underi < AR5416_4K_NUM_CTLS. Bounded.v4kEepromGetSpurChan:197-205:ixbound is HALASSERT-only in this function, but both callers loopi < AR5416_EEPROM_MODAL_SPURS(ar5416_attach.c:671-672, ar9280_attach.c:610-611) β no attacker-reachable OOB.- diag export :131-144 exports device-read + M_ZERO-zeroed data only β no uninitialized kernel memory disclosure (M_ZERO at ah_osdep.c:121-124).
eepromSwap:147-195: loops bounded by AR5416_4K_MAX_CHAINS=1 / AR5416_EEPROM_MODAL_SPURS=5.
Fix
fix.diff in this directory: ath_hal_free(ee) before each of the three
error returns (mirrors the DF-4873/DF-4913 family fix; git apply --check
clean against the pristine tree). Fix validation would require rebuilding the
guest kernel (ath_hal is compiled in, module symbol shadowing does not apply)
β not performed: the finding is a pure leak (no corruption) for which fix
validation is not mandated, and the PoC control path demonstrates the
free-on-error is exactly the installed-detach free.
PoC changes vs the initial seed
Adapted from the DF-4913 v14 harness (same KLD/sysctl pattern, moduledata_t
requirement, -I/usr/obj/usr/src/sys/X86_64_GENERIC include). One bug fixed
during bring-up: the control-path kprintf dropped its NFAIL argument
(-Werror format failure, build.log). v4k-specific deltas: include
ah_eeprom_v4k.h (owl_eep_start_loc 64, not 256), NWORDS from
struct ar5416eeprom_4k (188), error-return line numbers 324/366/385,
control version 0xE001 (AR5416_EEP_VER == 0xE, ah_eeprom_v14.h:44).
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_testableFix validation not performed: ath_hal is compiled into the guest kernel, so validating fix.diff requires a full kernel rebuild; the finding is a pure leak (no corruption) for which fix validation is not mandated. The PoC control path (valid image -> attach succeeds -> v4kEepromDetach frees) demonstrates the free path is balanced, and fix.diff (git apply --check clean) adds the same ath_hal_free(ee) on the three error returns.
fix.diff mirrors the verified control-path lifetime (malloc at :311 <-> v4kEepromDetach free at :272); identical to the DF-4873/DF-4913 family fix.
Confirmed kernel references
- sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:311
- sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:324
- sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:366
- sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:385
- sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:390
- sys/dev/netif/ath/ath_hal/ah_eeprom_v4k.c:267
- sys/dev/netif/ath/ath_hal/ar9002/ar9285_attach.c:246
- sys/dev/netif/ath/ath_hal/ar5416/ar5416_attach.c:507
- sys/dev/netif/ath/ath_hal/ah_internal.h:474
- sys/dev/netif/ath/ath_hal/ah_osdep.c:121
Detail
Evidence (decisive lines)
run.log (vmstat -m ath_hal 0 -> 600 blocks/675K, unchanged after kldunload), run.2.log (600 -> 1.17K blocks/1.32M: accumulates), console.log (per-mode '200 attaches, 0 HAL_OK, 200 with ah_eeprom==NULL (orphaned mallocs)' + balanced control + sizeof(HAL_EEPROM_v4k)=1152), build.log, env.txt, fix.diff
PoC changes
Adapted from the DF-4913 v14 harness (KLD + sysctl hw.v4kleak=1 trigger, moduledata_t, -I/usr/obj/usr/src/sys/X86_64_GENERIC). Fixed one bring-up bug: control-path kprintf dropped its NFAIL argument (-Werror=format). v4k deltas: ah_eeprom_v4k.h include (owl_eep_start_loc 64), NWORDS from struct ar5416eeprom_4k (188 words/376B), error lines 324/366/385, control image version 0xE001 (AR5416_EEP_VER 0xE).
Verified recommended fix
ath_hal_free(ee) before each of the three post-malloc error returns (HAL_EEREAD :324, HAL_EEBADSUM :366, HAL_EEBADSUM :385) β same fix as DF-4873/DF-4913.
Verdict
Kernel heap memory leak (permanent, 1152 bytes per failed attach) on the v4k EEPROM-attach error paths of ath_hal_v4kEepromAttach: the HAL_EEPROM_v4k malloc'd at ah_eeprom_v4k.c:311 is orphaned by three post-malloc returns β HAL_EEREAD at :324 (mid-image read failure), HAL_EEBADSUM at :366 (bad checksum) and :385 (bad version) β none of which free it, and AH_PRIVATE(ah)->ah_eeprom / ah_eepromDetach are installed only at :390-396 after every error return. The caller unwind (ar9285_attach.c:246-248 goto bad -> ah_detach -> ar5416Detach:507 -> ath_hal_eepromDetach macro, ah_internal.h:474-477) is a no-op for it. Third and final member of the eeprom-attach leak family (DF-4873 ar9287 ~1KB, DF-4913 v14 4.8KB verified live, DF-1995 ar5210), predicted by both prior verdicts. Reproduced on the guest by calling the real in-kernel function with a synthetic ath_hal whose eepromRead callback serves crafted v4k images (image base owl_eep_start_loc==64): 3 failure modes x 200 attaches orphaned 600 blocks / 675K of malloc type ath_hal (permanent across kldunload), a second trigger round stacked to 1.17K blocks / 1.32M, while 200 valid attach+detach control cycles left in-use unchanged. Attacker model: untrusted EEPROM / hostile ar9285 card (physical access or hotplug) with corrupt checksum/version β every attach fails identically, repeatable without bound.
No comments yet.