Missing runtime bounds check on gpio in 4/5 entry points (sibling of DF-2199)
Summary
ar5315GpioCfgOutput/CfgInput/Set/SetIntr all use caller-supplied gpio as bit-shift operand and MMIO field selector without any runtime upper-bound check. Only HALASSERT(gpio<AR_NUM_GPIO=7) guards three of them and HALASSERT compiled out of every standard build AH_ASSERT defined nowhere in sys/. ar5315GpioSetIntr has no check at all source comment /* XXX bounds check gpio */ at line 113. Only ar5315GpioGet (line 95) correctly guarded. Out-of-range gpio flows into AR5315_GPIODIR_M/O(gpio)=(1<<(x)) for gpio 7..31 writes stray bits into GPIODIR MMIO register for gpio>=32 1<<gpio is UB shift count>=width of int. ar5315GpioSet lines 80-81 reg &= ~(1<<gpio); reg |= (val&1)<<gpio; signed left shift of 1 with gpio>=31 signed-overflow UB gpio>=32 shift-count UB. ar5315GpioSetIntr line 116 val |= gpio << AR5315_GPIOINT_S ORs raw gpio into interrupt register gpio>=8 sets bits 8+ gpio overlapping GPIOINTLVL_S corrupts interrupt-level field. Reachable: root sysctl hw.athN.ledpin CTLFLAG_RW ath_sysctl_ledpin stores arbitrary int no bounds check when softled set calls ath_led_config->ath_hal_gpioCfgOutput/gpioset. Impact: corrupted bits in GPIO MMIO registers reconfiguring unrelated board GPIOs corrupted GPIO interrupt level/selection arming spurious/never-asserted interrupts interrupt-storm hang/stuck-state local DoS shift UB architecture/optimization-dependent. No arbitrary kernel-memory write found all sinks fixed-offset MMIO addresses inside GPIO register block practical ceiling local DoS/hardware misbehavior not privesc. On x86_64 HAL not loaded exposure to embedded AR5315-class boards where defect fully reachable.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2200 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able: add runtime gpio bounds check to 4 functions in ar5315_gpio.c | 1.3 KB | view raw |
| VERDICT.md | verdict | source-trace confirmation + mechanism + Medium-rated MMIO field-corruption rationale | 5.7 KB | β raw |
| build.sh | build-script | no-op (source-only) | 362 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 with all 3 sibling fixes applied (rc=0) | 5.6 MB | β download |
DF-2200 β ar5315 GPIO index unvalidated (4/5 entry points; field selector)
Verdict
REPRODUCED (source-only confirmation). Bug confirmed by source tracing of
sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c. No runtime PoC attempted
because this code is HW-gated (AH_SUPPORT_2316 || AH_SUPPORT_2317,
no Atheros NIC on guest).
Mechanism
ar5315GpioCfgOutput (line 42), ar5315GpioCfgInput (line 59),
ar5315GpioSet (line 77) validate gpio only with
HALASSERT(gpio < AR_NUM_GPIO) where AR_NUM_GPIO == 7 (line 31).
As confirmed in ah_internal.h:665-676, HALASSERT expands to nothing
unless AH_ASSERT is defined β and AH_ASSERT is only enabled in
sys/config/LINT64:1105, not in X86_64_GENERIC. In any production
build the four entry points accept any uint32_t gpio without bounds
checking.
The fifth entry point, ar5315GpioSetIntr (line 107), has no HALASSERT at
all β just the source comment /* XXX bounds check gpio */ at line 113.
Worse than its ar5312/ar5212 siblings, in ar5315 the unvalidated gpio is
used as a direct bit-shifted MMIO field selector, not just a per-pin
mask:
- val |= gpio << AR5315_GPIOINT_S; (line 116, with AR5315_GPIOINT_S 0
from ar5312reg.h:57) β gpio becomes bits [6:0] of the GPIOINT MMIO
register via AR5315_GPIOINT_M 0x3F. An out-of-range gpio corrupts
the AR5315_GPIOINTLVL field above it (AR5315_GPIOINTLVL_S 6,
ar5312reg.h:59) and can flip the level-sense bits, re-arming interrupts
on the wrong edge. This is more than a UB shift β it is direct MMIO
field corruption, which is why DF-2200 is rated Medium while its
siblings are Low.
- AR5315_GPIODIR_M(x) = 1 << (x) (ar5312reg.h:53), AR5315_GPIODIR_O(x)
= 1 << (x) (ar5312reg.h:54) β used as bit-mask in CfgOutput/Input.
For gpio >= 32, UB shift. For 7 β€ gpio < 32, mask/selects bits
outside the 7-pin GPIODIR field.
- (1 << gpio) at GpioSet (line 80-81) β UB shift for gpioβ₯32.
Only ar5315GpioGet (line 95) has a real runtime check
(if (gpio < AR_NUM_GPIO)).
Threat model / reachability
- Exported through
struct ath_halops vectors (ah.h:1477-1483). - Callers in
sys/dev/netif/ath/ath/if_ath_led.c,sys/dev/netif/ath/ath_hal/ar5212/ar5212_misc.c:157,167passsc->sc_ledpin/selectfrom EEPROM / dev config. - The ar5315 backend specifically targets the AR2316/AR2317 SoC platforms
(
#if (AH_SUPPORT_2316 || AH_SUPPORT_2317)at line 21). A malicious EEPROM feed produces MMIO field corruption on those platforms β local DoS, spurious interrupts, possible device lockup. The MMIO field corruption (vs. plain UB shift) is what elevates this above the Low siblings. No path to direct userspace control ofgpiowas found; the caller surface is driver-internal. - Severity Medium is appropriate per the rubric: realistic impact is local DoS / device misbehavior requiring specific HW (AR2316/2317 SoC) and a malicious EEPROM; field corruption is more dangerous than the siblings' pure UB shift.
Why no runtime PoC
- The audit guest has no Atheros NIC (
pciconf -lshows no ath device). ar5315_gpio.cis gated behind#if (AH_SUPPORT_2316 || AH_SUPPORT_2317)(line 21); neither symbol is auto-defined anywhere insys/(grepped) nor set byX86_64_GENERICorsys/conf/options. The file is also NOT insys/dev/netif/ath/ath_hal/MakefileSRCS. So the function bodies never link into the running kernel on this guest; the bug is a latent source-level defect in maintained SoC code.- 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 ar5315GpioSetIntr, 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.
See fix.diff (12 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.- The ar5212 sibling is in the Makefile SRCS and was rebuilt into
ath_hal.kowith-Werrorβ clean. - The ar5312/ar5315 files are NOT in the GENERIC Makefile SRCS (SoC-platform
files), so to truly exercise the patch they were additionally compiled
directly with
-DAH_SUPPORT_2316 -DAH_SUPPORT_2317(ar5315) and the per-arch include dir β rc=0, 0 errors, 0 warnings under-Werror.
Kernel references (confirmed during verification)
sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c:31βAR_NUM_GPIO 7sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c:42β HALASSERT only (CfgOutput)sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c:59β HALASSERT only (CfgInput)sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c:77β HALASSERT only (Set)sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c:95β real runtime check (Get)sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c:113β/* XXX bounds check gpio */(SetIntr, none at all)sys/dev/netif/ath/ath_hal/ar5312/ar5315_gpio.c:116βval |= gpio << AR5315_GPIOINT_S;β direct field selectorsys/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/ar5312/ar5312reg.h:53-60β GPIODIR / GPIOINT macros
Fix verification
fixedbatch build rc=0 -Werror + standalone -DAH_SUPPORT_2316 rc=0
batch build rc=0 -Werror + standalone -DAH_SUPPORT_2316 rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
HW-gated. Source-confirmed: ar5315 GPIO 4/5 funcs HALASSERT no-op. SetIntr uses gpio as direct MMIO field selector -> field corruption (Medium-rated).
Verified recommended fix
HW-gated. Source-confirmed: ar5315 GPIO 4/5 funcs HALASSERT no-op. SetIntr uses gpio as direct MMIO field selector -> field corruption (Medium-rated).
Verdict
HW-gated. Source-confirmed: ar5315 GPIO 4/5 funcs HALASSERT no-op. SetIntr uses gpio as direct MMIO field selector -> field corruption (Medium-rated).
No comments yet.