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

Missing/ineffective bounds checks on gpio pin in 4 of 5 entry points (UB in shift macros, register field corruption)

Summary

Four of five GPIO entry points fail to bounds-check gpio pin against AR_NUM_GPIO(6). ar5312GpioCfgOutput/CfgInput/Set rely solely on HALASSERT (ar5312_gpio.c:42 59 77) which ah_internal.h:675 confirms compiles to nothing in release builds (only active under #ifdef AH_ASSERT). ar5312GpioSetIntr has NO check at all -- source comment /* XXX bounds check gpio */ at line 113. Only ar5312GpioGet (line 95) does real runtime check. Out-of-range gpio flows directly into shift macros AR_GPIOCR_CR_A/CR_N/INT (ar5212reg.h:752-757) and into (1<<gpio)/((val&1)<<gpio) at lines 80-81 producing UB for gpio>=16 (CR shifts) gpio>=2^20 (INT shift) gpio>=32 (Set shifts) and corrupting bits in GPIOCR overlapping interrupt-select/int-enable/int-sense fields for any gpio in [6 15]. Reachability: sc->sc_ledpin settable from userland via writable sysctl dev.ath.X.ledpin (if_ath_sysctl.c:210-226 CTLTYPE_INT|CTLFLAG_RW no bounds check). When sc_softled set sysctl write immediately calls ath_led_config->ath_hal_gpioCfgOutput/gpioset(if_ath_led.c:128-130) reaching this file with arbitrary gpio. Root-gated. Worst-case DoS via register misconfiguration/UB no privilege escalation no kernel-memory-corruption primitive every OS_REG_WRITE targets fixed offset inside device MMIO window gpio only selects bit positions never addresses. Sibling ar5315_gpio.c carries identical defect pattern.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2199 Β· 6 files
FileTypeDescriptionSize
fix.diff suggested-fix git-apply-able: add runtime gpio bounds check to 4 functions in ar5312_gpio.c 1.3 KB view raw
VERDICT.md verdict source-trace confirmation + mechanism + threat model 5.6 KB ↓ raw
build.sh build-script no-op (source-only) 346 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
VERDICT.md verdict source-trace confirmation + mechanism + threat model
↓ download raw

DF-2199 β€” ar5312 GPIO index unvalidated (4/5 entry points)

Verdict

REPRODUCED (source-only confirmation). Bug confirmed by source tracing of sys/dev/netif/ath/ath_hal/ar5312/ar5312_gpio.c. No runtime PoC attempted because this code is HW-gated (only compiled for AR5312 SoC platforms that require AH_SUPPORT_AR5312, and there is no Atheros NIC on the audit guest).

Mechanism

ar5312GpioCfgOutput (line 42), ar5312GpioCfgInput (line 59), ar5312GpioSet (line 77) all validate gpio only via HALASSERT(gpio < AR_NUM_GPIO) where AR_NUM_GPIO == 6 (line 31).

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 (verified: the GENERIC ath lines are device ath, device ath_hal, options AH_SUPPORT_AR5416 β€” no AH_ASSERT). Concretely: in any production kernel build, HALASSERT(_x) expands to nothing, so the four CfgOutput/CfgInput/Set calls accept any uint32_t gpio value without bounds checking.

The fifth entry point, ar5312GpioSetIntr (line 107), is worse: it has no HALASSERT at all, just the source comment /* XXX bounds check gpio */ at line 113. 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 80-81) β†’ UB shift for gpioβ‰₯32

Only ar5312GpioGet (line 95) has a real runtime check (if (gpio < AR_NUM_GPIO)).

Threat model / reachability

  • The four vulnerable entry points are exported through struct ath_hal ops vectors (ah_gpioCfgOutput, ah_gpioCfgInput, ah_gpioSet, ah_gpioSetIntr) β€” ah.h:1477-1483.
  • Callers in sys/dev/netif/ath/ath/if_ath_led.c and sys/dev/netif/ath/ath_hal/ar5212/ar5212_misc.c:157,167 pass sc->sc_ledpin / select which originate from EEPROM / dev config.
  • A malicious or corrupted EEPROM could feed an out-of-range gpio, producing UB shifts and OOB MMIO field selection β€” local DoS / device misbehavior on AR5312 SoC platforms. No path to direct userspace control of gpio was found; this is a hardening gap with realistic-but-bounded impact (HW-gated, driver-internal caller surface).
  • Severity Low is appropriate per the rubric (narrow trigger, requires specific HW and a malicious EEPROM, no demonstrated escalation).

Why no runtime PoC

  • The audit guest has no Atheros NIC. pciconf -l shows no ath device.
  • ar5312_gpio.c is gated behind #ifdef AH_SUPPORT_AR5312 (line 21); the symbol is not auto-defined anywhere in sys/ (grepped) and not set by X86_64_GENERIC or sys/conf/options. The file is also NOT listed in sys/dev/netif/ath/ath_hal/Makefile SRCS (only ar5212_gpio.c is, at line 37). 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 that fires the moment a SoC platform build is done.
  • Source-only confirmation is therefore the strongest evidence available without bespoke HW, and matches the established pattern of the sibling finding DF-2098 (ar5416) which was also source-confirmed.

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 ar5312GpioSetIntr, 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 (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/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 (other than the usual missing-include-dirs noise).
  • The ar5212 sibling (ar5212_gpio.c) is in the Makefile SRCS and was rebuilt into ath_hal.ko with -Werror β€” clean.
  • The ar5312/ar5315 files are NOT in the GENERIC Makefile SRCS (they are SoC-platform files), so to truly exercise the patch they were additionally compiled directly with -DAH_SUPPORT_AR5312 / -DAH_SUPPORT_2316 -DAH_SUPPORT_2317 and the per-arch include dir β€” all three files: rc=0, 0 errors, 0 warnings under -Werror.

Kernel references (confirmed during verification)

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

batch build rc=0 -Werror + standalone -DAH_SUPPORT_AR5312 rc=0

batch build rc=0 -Werror + standalone -DAH_SUPPORT_AR5312 rc=0
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

HW-gated (no Atheros NIC). Source-confirmed: ar5312 GPIO 4/5 funcs use HALASSERT (no-op on GENERIC). ar5312GpioSetIntr has NO check at all.

Verified recommended fix

HW-gated (no Atheros NIC). Source-confirmed: ar5312 GPIO 4/5 funcs use HALASSERT (no-op on GENERIC). ar5312GpioSetIntr has NO check at all.

Verdict

HW-gated (no Atheros NIC). Source-confirmed: ar5312 GPIO 4/5 funcs use HALASSERT (no-op on GENERIC). ar5312GpioSetIntr has NO check at all.