ar5210GetDiagState HAL_DIAG_EEREAD dereferences args==NULL when SIOCGATHDIAG omits ATH_DIAG_IN β kernel page-fault panic (DF-4670 family; sibling of DF-1819, distinct deref)
Summary
The EEREAD case checks only argsize, then dereferences args - ath_ioctl_diag forwards ad_in_size unconditionally while allocating indata only under ATH_DIAG_IN, so ad_in_size=2 with no IN bit reads address 0. Discriminated from known DF-1819 (the *result write) via DYN keeping result valid. Identical deref in the generic ah.c:904 fallback. ATH_DIAGAPI+ar5210 gated (verified absent on guest; trigger builds+runs harmlessly). Reject-NULL-args two-file fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4705 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.c | β | 2.5 KB | view raw | |
| build.sh | β | 101 B | view raw | |
| run.sh | β | 98 B | view raw | |
| build.log | β | 134 B | view raw | |
| run.log | β | 134 B | view raw | |
| guest_session.log | β | 371 B | view raw | |
| env.txt | β | 714 B | view raw | |
| VERDICT.md | β | 2.7 KB | β raw | |
| fix.diff | β | 1012 B | view raw | |
| verdict.json | β | 3.3 KB | view raw |
VERDICT β DF-4705
status: inconclusive (hardware/option-gated; runtime confirmation impossible on the audit guest) Β· reproduced: 0 Β· analysis confidence: certain
Was it reproduced?
No. The QEMU audit guest has (all verified this run, see
guest_session.log):
- no Atheros PCI device (
pciconf -lhas no 0x168c vendor); - no
athinterface (ifconfig -aβ 0); - stock
X86_64_GENERIChas noATH_DIAGAPI/AH_PRIVATE_DIAGoptions (grep β 0 matches) and the shippedif_ath.kocontains noath_ioctl_diagsymbol (nm β 0), so theSIOCGATHDIAGentry point does not exist in the running kernel.
The trigger builds and runs on the guest (CC_EXIT=0,
RUN_EXIT=0, ioctl β -1 errno=6 ENXIO, no panic) β proving the
userspace side is correct and the failure is purely the missing
hardware/kernel-option gate, not the PoC.
Why the finding is real (code trace)
sys/dev/netif/ath/ath/if_ath_ioctl.c:174void *indata = NULL;:176insize = ad->ad_in_size;(attacker-controlled, taken regardless of flags):180-192indatais allocated onlyif (ad->ad_id & ATH_DIAG_IN); with the bit clear,args = indata = NULLwhileargsize = insize = 2is forwarded at:214intoath_hal_getdiagstate().sys/.../ar5210/ar5210_misc.c:638checks onlyargsize != sizeof(uint16_t)β passes.:642*(const uint16_t *)argsβ 2-byte read from address 0 β page fault β panic. The read happens while evaluating the call arguments ofath_hal_eepromRead()regardless of evaluation order; the PoC suppliesATH_DIAG_DYNso*resultis a valid kmalloc'd buffer β the only NULL deref on the line isargs(this is the discriminator against known DF-1819, whose trigger leaves*resultNULL by omitting DYN).- Identical deref in the generic fallback
ah.c:904(compiled without AH_PRIVATE_DIAG) β same NULL read reachable on all ath HALs.
Exploit chain
None β NULL read β controlled panic only (unprivileged local DoS, on double-gated configurations). No write primitive.
Fix validation
Not possible on this guest (baseline cannot be triggered without
ar5210 hardware + ATH_DIAGAPI). fix.diff is authored from the
line-accurate root cause above; fix_status: not_testable.
Guest state
Guest untouched except /root/poc/trigger{,.c} (removed after the
run); no panic, guest stayed up; guest_dirty: 0.
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 impossible on this guest: baseline requires ar5210 hardware + ATH_DIAGAPI kernel (both absent); fix.diff authored from line-accurate root cause and mirrors the DF-1819-style NULL guards extended to args
fix.diff (ar5210_misc.c + ah.c hunks); guest_session.log gate evidence
Confirmed kernel references
Detail
Evidence (decisive lines)
guest_session.log (uname, pciconf 0x168c absent, ifconfig 0 ath, GENERIC 0 ATH_DIAGAPI matches, nm if_ath.ko 0 ath_ioctl_diag, nm ath_hal.ko shows ar5210GetDiagState present); trigger.c (ATH_DIAG_DYN|HAL_DIAG_EEREAD, ad_in_size=2, no ATH_DIAG_IN); build.log CC_EXIT=0; run.log 'ioctl -> -1 errno=6 ... no panic'; VERDICT.md full trace if_ath_ioctl.c:174->ar5210_misc.c:638,642
PoC changes
seed trigger rewrote: trimmed struct ath_diag from if_athioctl.h (not in guest /usr/include), set ad_id=ATH_DIAG_DYN|HAL_DIAG_EEREAD (0x8011) with ad_in_size=2 and NO ATH_DIAG_IN so the ONLY NULL deref on line 642 is args (discriminates from DF-1819); ioctl request re-derived as _IOWR('i',138,...)
Verified recommended fix
reject NULL args/result in the HAL_DIAG_EEREAD case of ar5210GetDiagState and the generic ath_hal_getdiagstate fallback (see fix.diff)
Verdict
ar5210GetDiagState HAL_DIAG_EEREAD (ar5210_misc.c:642) dereferences args==NULL when SIOCGATHDIAG omits ATH_DIAG_IN while setting ad_in_size=2: ath_ioctl_diag (if_ath_ioctl.c:174-192) only allocates indata under ATH_DIAG_IN but forwards insize unconditionally, so the argsize check at :638 passes and (const uint16_t )args reads address 0 -> kernel page-fault panic. Distinct from known DF-1819 (the result NULL write in the same expression; this trigger supplies ATH_DIAG_DYN so result is valid) and the ar5210 instance of the DF-4670 args==NULL family; the unconditional generic fallback ah.c:904 has the identical deref. Runtime confirmation impossible on the audit guest: no Atheros PCI device, no ath interface, no ATH_DIAGAPI option in X86_64_GENERIC and no ath_ioctl_diag symbol in the stock if_ath.ko (all verified, guest_session.log); the trigger builds (CC_EXIT=0) and runs harmlessly (ENXIO) on the guest, proving the PoC side correct and the block purely hardware/option-gated.
No comments yet.