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

ar9287FillCapabilityInfo never enforces 2GHz-only Kiwi-2 silicon: GMODE divergence re-adds 11A/11NA wireless modes the EEPROM layer explicitly denies, and 5GHz channel edges are inherited β€” 5GHz channels admitted on 2GHz-only silicon (enabler of the DF-4793 NULL-pier panic)

Summary

The 9287 EEPROM layer refuses AMODE ('no 5GHz for Kiwi') but the inherited ar5416 GMODE block re-ORs 11A/11NA on every stock 11g card, edges are unconditional 4915-6100, and the ~20-field override never clamps - halWirelessModes is the sole HW gate in getchannels, so a 5GHz-permissive EEPROM regdomain admits 5GHz channels on 2GHz-only silicon: selecting one triggers the DF-4793 NULL-pier panic or off-calibration transmission. Exact DF-4799 (ar9285) twin; nf_5g IS populated here so the delta is caps/edges only. Clamp-the-A-family-modes fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4872 Β· 4 files
FileTypeDescriptionSize
VERDICT.md file 5.1 KB ↓ raw
env.txt file 272 B view raw
fix.diff file 1.0 KB view raw
verdict.json file 4.2 KB view raw
VERDICT.md file
↓ download raw

DF-4872 β€” ar9287Attach: 2GHz-only Kiwi-2 caps never enforced β€” GMODE divergence re-adds 11A/11NA modes and 5GHz edges survive; enables 5GHz channel selection on 2GHz-only silicon (upstream enabler of the DF-4793 NULL-pier panic)

What was verified (static, line-by-line; no AR9287 silicon on the audit guest)

The full chain from EEPROM opflag to 5GHz channel admission, every hop cited:

  1. The AR9287 EEPROM layer explicitly denies A-mode. sys/dev/netif/ath/ath_hal/ah_eeprom_9287.c:73-74 c case AR_EEP_AMODE: return HAL_EIO; /* no 5GHz for Kiwi */ The AR9287 EEPROM image itself carries only 2GHz calibration data: ah_eeprom_9287.h:135-153 β€” calFreqPier2G[AR9287_NUM_2G_CAL_PIERS], calPierData2G[AR9287_MAX_CHAINS][AR9287_NUM_2G_CAL_PIERS], and a single modalHeader ("only 2.4ghz here; so only one modal header entry", ah_eeprom_9287.c:187). There is no 5GHz pier/target data anywhere.

  2. The generic caps builder re-adds 11A anyway via the GMODE divergence. ar9287Attach β†’ ar9287FillCapabilityInfo (sys/dev/netif/ath/ath_hal/ar9002/ar9287_attach.c:321-324, 428-469) calls ar5416FillCapabilityInfo(ah) at line 433 and inherits: - ar5416_attach.c:904-915: when AR_EEP_GMODE is set (every functional 11g card: v9287EepromGet returns OK when opCapFlags & AR5416_OPFLAGS_11G, ah_eeprom_9287.c:75-79; identical polarity in ah_eeprom_v14.c:110-115 and ah_eeprom_v4k.c:83-90), lines 910-914 unconditionally OR HAL_MODE_11A | HAL_MODE_11NA_HT20 | HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS into halWirelessModes β€” even though the AMODE branch was denied. - ar5416_attach.c:920-921: halLow5GhzChan = 4915; halHigh5GhzChan = 6100; set unconditionally.

  3. ar9287FillCapabilityInfo never clamps. Its override block (ar9287_attach.c:435-466) sets ~20 fields (GPIO count, WoW, RIFS, STBC, SGI20, ...) but never touches halWirelessModes, halLow5GhzChan, or halHigh5GhzChan. Unlike the ar9285 twin (DF-4799), nf_5g is populated here (ar9287_attach.c:353-355), so the residual defect for the AR9287 is exactly the caps/edges divergence.

  4. halWirelessModes is the channel-admission gate. if_ath.c:6073-6084 ath_getradiocaps() β†’ ath_hal_getchannels(..., HAL_MODE_ALL, ...). In getchannels() (sys/dev/netif/ath/ath_hal/ah_regdomain.c:398-416): c modesAvail = ath_hal_getWirelessModes(ah); /* == halWirelessModes */ ... if ((cm->mode & modeSelect) == 0) continue; /* HAL_MODE_ALL passes */ if ((cm->mode & modesAvail) == 0) continue; /* <-- the only HW gate */ With the divergent 11A/11NA bits set, every 5GHz cmode passes; the 5GHz channel bitmap then comes from the EEPROM regdomain (ah_currentRD = regDmn[0], ar9287_attach.c:334-335, unvalidated u16). ar5212GetChannelEdges (ar5212_attach.c:646-655) hardcodes 4915/6100 for any 5GHZ-flagged mode, so 5GHz bands are admitted.

  5. Downstream consequence (already filed at the crash site as DF-4793). Selecting one of those 5GHz channels reaches ar9287SetPowerCalTable (ar9002/ar9287_reset.c): the 9287 EEPROM accessors return a NULL pier list for non-2GHz channels, and the unguarded per-chain walk NULL-derefs pList[0] in ath_ee_getLowerUpperIndex β†’ kernel panic. Even without the panic, the radio is programmed from 2GHz-only calibration data (regulatory misoperation), mirroring DF-4799's impact analysis for the AR9285.

Why no runtime PoC (untested, per family precedent DF-4793/DF-4799/DF-4548)

The trigger requires an AR9287 ("Kite-2") PCIe NIC whose EEPROM carries an 11G opflag (normal) plus a regdomain with 5GHz entries. The single-tenant QEMU/KVM audit guest has no Atheros NIC (no PCIe device assignment), so neither the panic nor the mis-tune can be exercised there. Verification is the static chain above; every hop was opened and read, no hop is assumed.

Threat model

Untrusted-EEPROM (same model as DF-4548/DF-4793/DF-4799): a card with a reprogrammed/corrupt EEPROM (physical access, resold hardware, or a malicious "compat" card). No EEPROM validation happens between the raw image and regDmn[0]/opCapFlags beyond the checksum, which the attacker recomputes. Note the GMODE hop needs nothing unusual at all β€” every stock 11g AR9287 satisfies it; only the regdomain content decides whether 5GHz channels appear.

Fix

Mask the A-family modes and invalidate the 5GHz edges in ar9287FillCapabilityInfo right after the inherit (see fix.diff). The cleaner companion fix is in ar5416_attach.c:904-915 β€” the GMODE block should not OR 11A/11NA bits; every 5416-family chip that supports 11A already gets those bits from the AMODE branch β€” but the per-chip clamp is the minimal, targeted change (matches the fix proposed for DF-4799).

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored from line-accurate root cause and verified git-apply-able; runtime fix validation impossible on the guest for the same reason the PoC is untested (no AR9287 hardware)

findings/poc/DF-4872/fix.diff
↓ fix.diffper-fix-DF-4872

Confirmed kernel references

Detail

Exploit chain

stock 11g opflag (or attacker-reprogrammed EEPROM, checksum recomputed) -> GMODE divergence adds 11A/11NA to halWirelessModes despite AMODE denial -> ar9287FillCapabilityInfo never clamps; 5GHz edges inherited -> ath_getradiocaps HAL_MODE_ALL (if_ath.c:6082) -> getchannels modesAvail gate passes 11A/11NA + EEPROM regdomain rd5GHz bitmaps -> 5GHz channels in ic_channels on 2GHz-only silicon -> channel switch -> DF-4793 NULL pier-list panic in ar9287SetPowerCalTable (or uncalibrated out-of-band radio programming)

Evidence (decisive lines)

findings/poc/DF-4872/VERDICT.md (full static chain with path:line at every hop); findings/poc/DF-4872/fix.diff (git apply --check clean)

PoC changes

No runtime PoC possible: trigger hardware (AR9287 Kite-2 PCIe NIC + its EEPROM) absent from the QEMU guest; verification is the static line-by-line chain in VERDICT.md

Verified recommended fix

In ar9287FillCapabilityInfo, immediately after ar5416FillCapabilityInfo succeeds, mask HAL_MODE_11A|TURBO|108A|11A_HALF|11A_QUARTER|11NA_HT20|11NA_HT40PLUS|11NA_HT40MINUS out of halWirelessModes and zero halLow5GhzChan/halHigh5GhzChan (see fix.diff)

Verdict

Hardware-bound caps/regulatory defect, no memory corruption in this file: ar9287FillCapabilityInfo (ar9287_attach.c:433) inherits ar5416FillCapabilityInfo, whose GMODE block (ar5416_attach.c:904-915) unconditionally ORs HAL_MODE_11A|11NA_HT20|11NA_HT40+|11NA_HT40- into halWirelessModes whenever the EEPROM 11G opflag is set (true on every functional 11g card, polarity verified uniform in v14/v4k/9287 eeprom layers) even though the 9287 EEPROM layer explicitly denies AR_EEP_AMODE ('no 5GHz for Kiwi', ah_eeprom_9287.c:73-74); 5GHz edges 4915/6100 are set unconditionally (ar5416_attach.c:920-921). The override block (ar9287_attach.c:435-466) sets ~20 caps fields but never clamps halWirelessModes or the 5GHz edges. halWirelessModes is the sole hardware gate admitting 11A/11NA cmodes in getchannels (ah_regdomain.c:398-416 'cm->mode & modesAvail'), so an EEPROM regDmn[0] (taken unvalidated at ar9287_attach.c:334-335) with 5GHz entries yields 5GHz channels on 2GHz-only silicon; selecting one hits the already-filed DF-4793 NULL-pier panic in ar9287SetPowerCalTable or programs the RF from 2GHz-only cal data (ar9287 EEPROM image has no 5GHz structures, ah_eeprom_9287.h:135-153). Exact DF-4799 (ar9285) twin shape; unlike ar9287, nf_5g IS populated here (ar9287_attach.c:353-355). Static chain verified hop-by-hop; no AR9287 NIC on the QEMU guest, so untested at runtime per family precedent (DF-4793/DF-4799/DF-4548).