DF-4956 / verdict.json
{ "finding_id": "DF-4956", "status": "reproduced", "reproduced": 1, "impact": "leak", "confidence": "certain", "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 mallocd at ah_eeprom_v4k.c:311 is orphaned by three post-malloc returns \u2014 HAL_EEREAD at :324 (mid-image read failure), HAL_EEBADSUM at :366 (bad checksum) and :385 (bad version) \u2014 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 \u2014 every attach fails identically, repeatable without bound.", "exploit_chain": "", "evidence": "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", "kernel_refs": [ "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" ], "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).", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 240, "guest_dirty": 1, "build_cmd": "cd /root/DF-4956 && make (KLD v4kleak.ko, cc 8.3 [DragonFly])", "run_cmd": "kldload ./v4kleak.ko && sysctl hw.v4kleak=1 && vmstat -m | grep -i ath_hal && kldunload v4kleak && vmstat -m | grep -i ath_hal", "code_hash": "3bbfef400952814e76f54ee54080e39a5cc9ef0f58eba7eed596b2f19e5e7b1a", "notes": "Leak is permanent and accumulates per attach round (600->1200 blocks). Same family/fix as DF-4873 (ar9287) and DF-4913 (v14); completes the family \u2014 every eeprom variant (v1 DF-1995, v4k DF-4956, v14 DF-4913, 9287 DF-4873) now has the attach-leak filed; v3 legacy and ar9300 use different shapes. ath_hal is compiled into the guest kernel, so the PoC needed no module load of the HAL itself.", "fix_status": "not_applicable", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Fix 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_evidence": "fix.diff mirrors the verified control-path lifetime (malloc at :311 <-> v4kEepromDetach free at :272); identical to the DF-4873/DF-4913 family fix.", "recommended_fix": "ath_hal_free(ee) before each of the three post-malloc error returns (HAL_EEREAD :324, HAL_EEBADSUM :366, HAL_EEBADSUM :385) \u2014 same fix as DF-4873/DF-4913." } |