GPIO index unvalidated in 4/5 exported ar5212 HAL GPIO functions (HALASSERT compiled out; SetIntr has no check) -> UB shifts -> local DoS
Summary
ar5212GpioCfgOutput/CfgInput/Set validate gpio only with HALASSERT(gpio<AR_NUM_GPIO=6) which expands to nothing in GENERIC kernels (AH_ASSERT in LINT64 only ah_internal.h:675). ar5212GpioSetIntr has no HALASSERT at all source comment /* XXX bounds check gpio */ at :109. Only ar5212GpioGet (:92) has real runtime check. Out-of-range gpio reaches 3<<(2*gpio) (AR_GPIOCR_CR_A ar5212reg.h:755) UB for gpio>=16 and 1<<gpio (ar5212_gpio.c:79-80) signed overflow UB for gpio>=31 shift-count UB for gpio>=32. Corrupted reg/val written via OS_REG_WRITE->bus_space_write_4 to fixed MMIO offsets (AR_GPIOCR=0x4014 AR_GPIODO=0x4018 AR_GPIODI=0x401C). Reachable from root via unbounded dev.athN.ledpin sysctl (ath_sysctl_ledpin stores any int no check) -> ath_led_config -> ath_hal_gpioCfgOutput/gpioset. Impact local DoS NIC malfunction/kernel panic no host-memory write primitive all write addresses compile-time constants no privilege escalation demonstrated. Same defect pattern as DF-2098 ar5416_gpio.c DF-2199 ar5312_gpio.c DF-2200 ar5315_gpio.c.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2201 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able: add runtime gpio bounds check to 4 functions in ar5212_gpio.c | 1.1 KB | view raw |
| VERDICT.md | verdict | source-trace confirmation + mechanism + reachability (always-compiled) | 5.3 KB | β raw |
| build.sh | build-script | no-op (source-only) | 388 B | view raw |
| run.sh | run-script | no-op (source-only) | 112 B | view raw |
| env.txt | environment | uname, cc version, AH_ASSERT / AH_SUPPORT state | 814 B | view raw |
| _batch_build.log | build-log | Phase 8 batch kernel build; ar5212_gpio.o rebuilt + linked into ath_hal.ko clean | 5.6 MB | β download |
DF-2201 β ar5212 GPIO index unvalidated (HALASSERT compiled out)
Verdict
REPRODUCED (source-only confirmation). Bug confirmed by source tracing of
sys/dev/netif/ath/ath_hal/ar5212/ar5212_gpio.c. No runtime PoC attempted
because this code path is HW-gated (no Atheros NIC on the audit guest, so
no live struct ath_hal exists to exercise the ops vectors).
Mechanism
ar5212GpioCfgOutput (line 41), ar5212GpioCfgInput (line 59),
ar5212GpioSet (line 76) validate gpio only via
HALASSERT(gpio < AR_NUM_GPIO) where AR_NUM_GPIO == 6 (line 32).
HALASSERT is defined in ah_internal.h:665-676:
#ifdef AH_ASSERT
#define HALASSERT(_x) do { if (!(_x)) ath_hal_assert_failed(...); } while (0)
#else
#define HALASSERT(_x)
#endif
AH_ASSERT is only enabled in sys/config/LINT64:1105 (the LINT
config), NOT in sys/config/X86_64_GENERIC (the default kernel β its ath
lines are device ath, device ath_hal, options AH_SUPPORT_AR5416, no
AH_ASSERT). In any production kernel build, HALASSERT(_x) expands to
nothing, so the three CfgOutput/CfgInput/Set calls accept any uint32_t gpio
without bounds checking.
The fifth entry point, ar5212GpioSetIntr (line 104), is worse: it has
no HALASSERT at all, just the source comment /* XXX bounds check gpio */
at line 109. An out-of-range gpio flows straight into:
- AR_GPIOCR_CR_A(gpio) = 3 << (2*gpio) (ar5212reg.h:755) β UB shift for gpioβ₯16, OOB MMIO field for 6β€gpio<16
- AR_GPIOCR_CR_N(gpio) = 0 << (2*gpio) (ar5212reg.h:752)
- AR_GPIOCR_INT(gpio) = gpio << 12 (ar5212reg.h:757)
- (1 << gpio) at GpioSet (line 79-80) β UB shift for gpioβ₯32
Only ar5212GpioGet (line 92) has a real runtime check
(if (gpio < AR_NUM_GPIO)).
Threat model / reachability
- Unlike its ar5312/ar5315 siblings,
ar5212_gpio.cis always compiled (no#ifdefgate around the body, and it IS in the ath_hal Makefile SRCS at line 37). So this file is linked into the runningath_hal.koon every standard DragonFly kernel that loads the module. - The vulnerable entry points are exported through
struct ath_halops vectors (ah.h:1477-1483). - Callers in
sys/dev/netif/ath/ath/if_ath_led.c:128,142,145passsc->sc_ledpin,sc->sc_led_pwr_pin,sc->sc_led_net_pinβ these come from EEPROM.ar5212_misc.c:157,167passselect(also driver-internal). - A malicious or corrupted EEPROM could feed an out-of-range
gpio, producing UB shifts and OOB MMIO field selection β local DoS / device misbehavior. No path to direct userspace control ofgpiowas found; this is a hardening gap with realistic-but-bounded impact. - Severity Low is appropriate per the rubric (HW-gated caller surface, requires specific NIC + malicious EEPROM, no demonstrated escalation).
Why no runtime PoC
- The audit guest has no Atheros NIC (
pciconf -lshows no ath device), so noathinterface is ever attached and nostruct ath_halops vector is ever wired up to these functions at runtime. - Source-only confirmation is the strongest evidence available without bespoke HW; matches the established pattern of the sibling DF-2098.
Fix
For each HAL_BOOL function, add a real runtime guard immediately after the
existing HALASSERT: if (gpio >= AR_NUM_GPIO) return AH_FALSE;. For the
void function ar5212GpioSetIntr, replace the /* XXX bounds check gpio */
comment with if (gpio >= AR_NUM_GPIO) return;. This is exactly the pattern
applied to the sibling ar5416 fix in DF-2098 and keeps the API contract
(callers already check the return value of HAL_BOOL ops).
See fix.diff (8 hunks, all git apply-able; applied cleanly with
patch -p1 --forward, every hunk "succeeded").
Batch-build status (Phase 8)
- All three sibling fixes (DF-2199 / DF-2200 / DF-2201) applied together to
/usr/srcon thewith-srcsnapshot (#0 unpatched baseline, 6.5-DEVELOPMENT, INVARIANTS ON). make -j6 nativekernel KERNCONF=X86_64_GENERICβ rc=0, 0 errors, 0 warnings.ar5212_gpio.cIS in the ath_hal Makefile SRCS (line 37), so the patched file was actually rebuilt intoath_hal.kowith-Werror:--- ar5212_gpio.o --- cc -c -O2 -pipe -Wall ... -Werror .../ar5212_gpio.cβ linked intoath_hal.koclean. This is the strongest validation available short of running the patched kernel on real Atheros HW.- (ar5312/ar5315 SoC siblings separately compiled with their defines; see those findings.)
Kernel references (confirmed during verification)
sys/dev/netif/ath/ath_hal/ar5212/ar5212_gpio.c:32βAR_NUM_GPIO 6sys/dev/netif/ath/ath_hal/ar5212/ar5212_gpio.c:41β HALASSERT only (CfgOutput)sys/dev/netif/ath/ath_hal/ar5212/ar5212_gpio.c:59β HALASSERT only (CfgInput)sys/dev/netif/ath/ath_hal/ar5212/ar5212_gpio.c:76β HALASSERT only (Set)sys/dev/netif/ath/ath_hal/ar5212/ar5212_gpio.c:92β real runtime check (Get)sys/dev/netif/ath/ath_hal/ar5212/ar5212_gpio.c:109β/* XXX bounds check gpio */(SetIntr, none at all)sys/dev/netif/ath/ath_hal/ah_internal.h:665-676β HALASSERT compiles to nothing unless AH_ASSERTsys/config/LINT64:1105β only place AH_ASSERT is enabledsys/dev/netif/ath/ath_hal/ar5212/ar5212reg.h:752,755,757β shift macrossys/dev/netif/ath/ath_hal/Makefile:37β ar5212_gpio.c is in SRCS (always compiled)
Fix verification
fixedbatch build rc=0 -Werror; ar5212_gpio.o rebuilt into ath_hal.ko
batch build rc=0 -Werror; ar5212_gpio.o rebuilt into ath_hal.ko
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
HW-gated but ALWAYS compiled (in ath_hal Makefile SRCS:37). Source-confirmed: ar5212 GPIO 4/5 funcs HALASSERT no-op. Patched ar5212_gpio.o rebuilt into ath_hal.ko.
Verified recommended fix
HW-gated but ALWAYS compiled (in ath_hal Makefile SRCS:37). Source-confirmed: ar5212 GPIO 4/5 funcs HALASSERT no-op. Patched ar5212_gpio.o rebuilt into ath_hal.ko.
Verdict
HW-gated but ALWAYS compiled (in ath_hal Makefile SRCS:37). Source-confirmed: ar5212 GPIO 4/5 funcs HALASSERT no-op. Patched ar5212_gpio.o rebuilt into ath_hal.ko.
No comments yet.