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

ar5416GpioGet reads wrong GPIO input bit on AR9287 (KIWI) due to missing else

Field Value
ID DF-2099
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:L
CWE CWE-670 Control Flow Implementation of Extra Step; CWE-480 Use of Incorrect Operator
File sys/dev/netif/ath/ath_hal/ar5416/ar5416_gpio.c
Lines 192-200
Area net/ath
Confidence certain
Discovered 2026-07-25
Reported pending
Known CVE none
CVE match novel

Summary

In ar5416GpioGet the chip-revision dispatch chain is broken: line 194 uses if instead of else if. Because AR_XSREV_VERSION_KIWI (0x180) is numerically >= AR_XSREV_VERSION_KITE (0xC0), any KIWI (AR9287) chip passes both AR_SREV_KIWI_10_OR_LATER and AR_SREV_KITE_10_OR_LATER, so the correct AR9287_GPIO_IN_VAL extraction is immediately overwritten with the AR9285_GPIO_IN_VAL mask/shift. ar5416GpioGet therefore returns the wrong input bit on AR9287 hardware. No security boundary is crossed (GPIO input state on a wifi NIC is not a privilege/security surface); reported as a reproducible correctness defect in the audited file.

Root cause

ar5416_gpio.c:192-199:

if (AR_SREV_KIWI_10_OR_LATER(ah))                       // macVersion >= 0x180
    bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9287_GPIO_IN_VAL);
if (AR_SREV_KITE_10_OR_LATER(ah))                       // <-- BUG: 'if', not 'else if'
    bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9285_GPIO_IN_VAL);
else if (AR_SREV_MERLIN_10_OR_LATER(ah))
    bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR928X_GPIO_IN_VAL);
else
    bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR_GPIO_IN_VAL);

The macros (ar5416reg.h:740,757,785) are simple >= version comparisons, so a KIWI chip (0x180) satisfies KITE_10_OR_LATER (>=0xC0) and MERLIN_10_OR_LATER (>=0x80). The second if executes unconditionally after the KIWI branch and overwrites bits with MS(reg, AR9285_GPIO_IN_VAL) (mask 0x00FFF000, shift 12, ar5416reg.h:565-566) instead of the correct AR9287 mask (0x003FF800, shift 11, ar5416reg.h:567-568). The final return ((bits & AR_GPIO_BIT(gpio)) != 0) (ar5416_gpio.c:200) thus tests a different bit position than intended on AR9287.

Threat model & preconditions

  • Attacker position: N/A β€” functional correctness defect.
  • Privileges gained or impact: none β€” on AR9287 (KIWI) NICs, callers of ath_hal_gpioget (wrapped at if_athvar.h:1468) observe a GPIO input bit taken from the wrong field of AR_GPIO_IN_OUT. Worst case: a misread hardware GPIO input (e.g. an external signal sampled via the NIC GPIO header) is interpreted incorrectly.
  • Required config or capabilities: AR9287 (KIWI) hardware.
  • Reachability: ar5416GpioGet appears to have no in-tree caller in the ath(4) driver itself today, further limiting impact to out-of-tree/HAL-tool consumers.

Proof of concept

Not a security exploit; included for completeness and reproducibility. On AR9287 hardware, call ath_hal_gpioget(ah, n) for any pin n in [0, halNumGpioPins) and compare against the datasheet value of AR_GPIO_IN_OUT[11..21] (AR9287_GPIO_IN_VAL). The returned bit will instead reflect AR_GPIO_IN_OUT[12..23] (AR9285 layout). A unit-style check: read AR_GPIO_IN_OUT once, manually extract (val & 0x003FF800) >> 11, and diff against the function's return for each pin; mismatch on AR9287 confirms the bug.

Impact

Functional correctness only. No panic, no leak.

Change the if at ar5416_gpio.c:194 to else if so the dispatch is mutually exclusive:

--- a/sys/dev/netif/ath/ath_hal/ar5416/ar5416_gpio.c
+++ b/sys/dev/netif/ath/ath_hal/ar5416/ar5416_gpio.c
@@ -192,7 +192,7 @@ ar5416GpioGet(struct ath_hal *ah, uint32_t gpio)
     */
    if (AR_SREV_KIWI_10_OR_LATER(ah))
        bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9287_GPIO_IN_VAL);
-   if (AR_SREV_KITE_10_OR_LATER(ah))
+   else if (AR_SREV_KITE_10_OR_LATER(ah))
        bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9285_GPIO_IN_VAL);
    else if (AR_SREV_MERLIN_10_OR_LATER(ah))
        bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR928X_GPIO_IN_VAL);

References

Timeline

  • 2026-07-25 Discovered during automated audit.
  • 2026-07-25 Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2099 Β· 2 files
FileTypeDescriptionSize
fix.diff suggested-fix git-apply-able fix 1.5 KB view raw
VERDICT.md verdict source-trace confirmation 579 B ↓ raw
VERDICT.md verdict source-trace confirmation
↓ download raw

DF-2099 β€” ar5416GpioGet wrong GPIO bit on AR9287 (KIWI)

Verdict

REPRODUCED (source-only confirmation). Bug confirmed by source tracing.

Mechanism

ar5416GpioGet (ar5416_gpio.c:192-195): line 194 uses 'if' not 'else if'. AR_XSREV_VERSION_KIWI(0x180) >= AR_XSREV_VERSION_KITE(0xC0), so KIWI chips pass both checks: line 193 sets correct AR9287_GPIO_IN_VAL, then line 194 overwrites with wrong AR9285_GPIO_IN_VAL.

Fix

Change 'if' to 'else if' at line 194.

Batch-build status

Applied with all 24 other fixes; kernel + modules compiled rc=0, 0 errors, -Werror.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Changed if to else if; batch build rc=0.

Changed if to else if; batch build rc=0.
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

ar5416GpioGet uses if not else if; AR9287 overwrites wrong field.

Verified recommended fix

ar5416GpioGet uses if not else if; AR9287 overwrites wrong field.

Verdict

ar5416GpioGet uses if not else if; AR9287 overwrites wrong field.