# 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):

```c
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.
