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

ath_intr TDMA/SWBA branch: unchecked TAILQ_FIRST(&ic->ic_vaps) and NULL vap->iv_tdma dereference in hard interrupt context (sc_tdma outlives the TDMA vap)

Summary

On HAL_INT_SWBA with sc_tdma set, ath_intr takes TAILQ_FIRST(&ic->ic_vaps) with no NULL check, passes it to ath_tdma_beacon_send, then evaluates vap->iv_tdma->tdma_bintval. iv_tdma is allocated only for TDMA vaps and is NULL for every other vap type. sc_tdma is cleared in ath_vap_delete only when sc_nvaps==0, so destroying the TDMA vap while a sibling vap remains leaves sc_tdma=1 with a NULL-iv_tdma vap at the list head - the next SWBA NULL-derefs in hard interrupt context. Entry requires admin-level vap create/destroy ordering (create TDMA vap + sibling vap, destroy TDMA vap, wait <= one beacon interval) - deterministic, no timing luck; a teardown race variant needs SMP timing during any vap destroy. Not runnable on the guest (no ath NIC - SWBA can never be raised).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3617 Β· 6 files
FileTypeDescriptionSize
README.md β€” 2.8 KB ↓ raw
VERDICT.md β€” 2.5 KB ↓ raw
env.txt β€” 388 B view raw
fix.diff β€” 1.0 KB view raw
manifest.json β€” 1.2 KB view raw
verdict.json β€” 2.4 KB view raw

DF-3617 β€” ath_intr TDMA/SWBA path: unchecked TAILQ_FIRST(&ic->ic_vaps) and iv_tdma NULL-deref

The defect

sys/dev/netif/ath/ath/if_ath.c:2182-2193 (ath_intr, HAL_INT_SWBA):

if (sc->sc_tdma) {
        if (sc->sc_tdmaswba == 0) {
                struct ieee80211com *ic = &sc->sc_ic;
                struct ieee80211vap *vap =
                    TAILQ_FIRST(&ic->ic_vaps);
                ath_tdma_beacon_send(sc, vap);          /* derefs vap */
                sc->sc_tdmaswba =
                    vap->iv_tdma->tdma_bintval;         /* derefs iv_tdma */

Two unguarded dereferences on the hard-interrupt path:

  1. TAILQ_FIRST(&ic->ic_vaps) may be NULL β€” ath_tdma_beacon_send (sys/dev/netif/ath/ath/if_ath_tdma.c:615) immediately dereferences the vap (avp->av_bcbuf via ATH_VAP(vap)) β†’ NULL-deref panic in hard interrupt context.

  2. vap->iv_tdma may be NULL for a non-TDMA vap β€” iv_tdma is allocated ONLY in ieee80211_tdma_vattach (sys/netproto/802_11/wlan/ieee80211_tdma.c:197) and is NULL for every other vap type β†’ NULL->tdma_bintval panic.

Reachability paths

(a) Config-ordering (not even a narrow race): sc->sc_tdma is cleared in ath_vap_delete only when sc->sc_nvaps == 0 (if_ath.c:1855-1858). If the TDMA vap is destroyed while any other vap still exists, sc_tdma stays set while the vaps list now heads with a non-TDMA vap (or the remaining vap is itself deleted later leaving the list momentarily empty) β€” the next SWBA interrupt takes the TDMA branch and dereferences either NULL iv_tdma or a NULL vap.

(b) Teardown race: ath_vap_delete disables interrupts at if_ath.c:1785 (ath_hal_intrset(ah, 0)), but an ath_intr already past ath_hal_intrpend (if_ath.c:2090) on another CPU keeps executing; the SWBA handling at 2175+ runs after the PCU lock was dropped at 2163, so nothing serializes it against ieee80211_vap_detach emptying ic_vaps (1793) or against sc_tdma = 0 (1855). Window is milliseconds wide (TXQ drains), not microseconds.

Preconditions

Atheros NIC with TDMA-capable HAL (mac version > 0x78, if_ath.c:1028), IEEE80211_SUPPORT_TDMA built, a TDMA (AHDEMO+CLONE_TDMA) vap created, and either the vap-deletion ordering above or the teardown race. All vap create/destroy operations require admin (ifconfig); the panic itself then fires from interrupt context.

Why inconclusive here

The audit guest has no Atheros hardware (env.txt): SWBA interrupts cannot be generated, so neither the config-ordering path nor the race can be executed. Source-level certainty is the bar for this file; the dereferences and the flag-lifetime defect are proven above from the cited lines.

Fix

fix.diff guards both dereferences in the ISR (minimal, safe in hard interrupt context) β€” see VERDICT.md for the companion sc_tdma lifetime note.

VERDICT.md
↓ download raw

DF-3617 β€” VERDICT

Status: inconclusive (NULL-deref/panic class defect proven from source; cannot execute on a guest with no Atheros NIC)

Reproduced?

No. The defect fires from ath_intr() on HAL_INT_SWBA, which requires the wireless hardware to raise a software-beacon-alert interrupt. The guest has no ath device (env.txt: no ath PCI id, no dev.ath sysctl, if_ath.ko present but unloaded), so no SWBA can ever arrive. No amount of userland construction on this guest reaches the code.

Why the defect is certain anyway

  • The dereferences are unconditional in the TDMA SWBA branch (if_ath.c:2186-2190): TAILQ_FIRST(&ic->ic_vaps) result passed straight into ath_tdma_beacon_send() (which dereferences ATH_VAP(vap) β†’ avp->av_bcbuf, if_ath_tdma.c:615ff) and then vap->iv_tdma->tdma_bintval.
  • iv_tdma is non-NULL only for TDMA vaps (sys/netproto/802_11/wlan/ieee80211_tdma.c:197 sets it in ieee80211_tdma_vattach; :211 clears it at detach; every other vap type leaves it NULL).
  • sc->sc_tdma outlives the TDMA vap: ath_vap_delete clears it only when sc->sc_nvaps == 0 (if_ath.c:1855-1858). Delete the TDMA vap while any sibling vap remains β†’ sc_tdma==1 with a non-TDMA (iv_tdma==NULL) vap at the head of ic_vaps β†’ guaranteed NULL deref on the next SWBA. This path needs no timing luck, only vap create/destroy ordering (admin-triggered; result is a kernel panic β†’ local DoS).
  • The teardown-race variant: ath_hal_intrset(ah,0) at if_ath.c:1785 does not stop an ISR already executing; SWBA processing at 2175+ holds no lock that ieee80211_vap_detach (1793) or sc_tdma=0 (1855) respects.

Impact

NULL-deref panic in hard interrupt context (kernel DoS). Not memory corruption (no wild write), no info leak. Requires admin-level vap reconfiguration on a TDMA-configured ath interface to reach β€” hence Low.

Fix

fix.diff: guard vap == NULL || vap->iv_tdma == NULL in the SWBA TDMA branch (skip the beacon send, leave sc_tdmaswba at 0 so the next SWBA retries). Companion (recommended, not in the minimal diff): clear sc_tdma when the last TDMA vap is destroyed rather than when sc_nvaps hits 0 β€” track it at if_ath.c:1840-1859 alongside sc_nstavaps.

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 adds ISR-side guards; applies clean via git apply --check but no ath hardware exists to run baseline/patched comparison

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

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md/VERDICT.md line-cited traces; env.txt (no ath hardware); fix.diff applies clean (git apply --check)

PoC changes

n/a - no seeded PoC; analysis-only pack (hardware-interrupt path, no HW on guest)

Verified recommended fix

Guard vap==NULL/iv_tdma==NULL in the SWBA TDMA branch and clear sc_tdma on last TDMA vap destruction

Verdict

ath_intr's HAL_INT_SWBA TDMA branch (if_ath.c:2186-2190) passes TAILQ_FIRST(&ic->ic_vaps) unchecked into ath_tdma_beacon_send (which dereferences ATH_VAP(vap)->av_bcbuf) and then dereferences vap->iv_tdma->tdma_bintval. iv_tdma is NULL for every non-TDMA vap (wlan/ieee80211_tdma.c:197 allocates it only for TDMA vaps). sc_tdma is cleared only when sc_nvaps==0 (if_ath.c:1855-1858), so destroying the TDMA vap while a sibling vap remains leaves sc_tdma=1 with a NULL-iv_tdma vap at the list head: the next SWBA interrupt NULL-derefs in hard interrupt context (deterministic given the vap ordering; no timing luck needed). A teardown race variant also exists (an ISR already past ath_hal_intrpend at 2090 keeps running after ath_hal_intrset(ah,0) at 1785; SWBA processing holds no lock that vap detach respects). Not executable on the audit guest: no Atheros NIC means no SWBA can ever be raised (env.txt). Impact class: kernel panic / local DoS requiring admin-level TDMA vap reconfiguration.