β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2201

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2201 Β· 6 files
FileTypeDescriptionSize
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
VERDICT.md verdict source-trace confirmation + mechanism + reachability (always-compiled)
↓ download raw

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.c is always compiled (no #ifdef gate around the body, and it IS in the ath_hal Makefile SRCS at line 37). So this file is linked into the running ath_hal.ko on every standard DragonFly kernel that loads the module.
  • The vulnerable entry points are exported through struct ath_hal ops vectors (ah.h:1477-1483).
  • Callers in sys/dev/netif/ath/ath/if_ath_led.c:128,142,145 pass sc->sc_ledpin, sc->sc_led_pwr_pin, sc->sc_led_net_pin β€” these come from EEPROM. ar5212_misc.c:157,167 pass select (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 of gpio was 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 -l shows no ath device), so no ath interface is ever attached and no struct ath_hal ops 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/src on the with-src snapshot (#0 unpatched baseline, 6.5-DEVELOPMENT, INVARIANTS ON).
  • make -j6 nativekernel KERNCONF=X86_64_GENERIC β†’ rc=0, 0 errors, 0 warnings.
  • ar5212_gpio.c IS in the ath_hal Makefile SRCS (line 37), so the patched file was actually rebuilt into ath_hal.ko with -Werror: --- ar5212_gpio.o --- cc -c -O2 -pipe -Wall ... -Werror .../ar5212_gpio.c β†’ linked into ath_hal.ko clean. 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)

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

batch 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
↓ fix.diffcombined build rc=0

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.