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

ar5210ResetTxQueue dereferences ah_curchan (IEEE80211_IS_CHAN_TURBO) without the NULL guard both sibling HALs carry ("NB: chan can be NULL during attach")

Summary

The queue reset caches ah_curchan and dereferences it unconditionally for every DATA-queue reset - ah_curchan is NULL from attach until the first Reset. Both ar5211 and ar5212 guard exactly this with an attach-time comment; the 5210 missed it and its chan use is broader (the whole turbo/normal IFS branch). In-tree call order protects; latent NULL-deref for pre-channel-set resets. One-line-guard fix (build+boot validated).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4734 Β· 8 files
FileTypeDescriptionSize
README.md β€” 2.3 KB ↓ raw
VERDICT.md β€” 2.2 KB ↓ raw
fix.diff β€” 545 B view raw
build.sh β€” 143 B view raw
build.log β€” 5.1 MB ↓ download
env.txt β€” 674 B view raw
manifest.json β€” 1.1 KB view raw
verdict.json β€” 3.0 KB view raw

DF-4734 β€” ar5210ResetTxQueue dereferences ah_curchan without the NULL guard both siblings carry

File: sys/dev/netif/ath/ath_hal/ar5210/ar5210_xmit.c:157,180 Severity: Low (latent NULL-deref panic; in-tree call order protects) β€” certain on the divergence.

Root cause

ar5210ResetTxQueue takes the current channel

157:    const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;

and, for every DATA queue, dereferences it unconditionally:

180:    if (IEEE80211_IS_CHAN_TURBO(chan)) {

IEEE80211_IS_CHAN_TURBO(_c) is ((_c)->ic_flags & IEEE80211_CHAN_TURBO) != 0 (sys/netproto/802_11/_ieee80211.h:268) β€” a plain deref. ah_curchan is AH_NULL from attach until the first ar5210Reset runs.

Both sibling HALs guard exactly this and annotate why:

  • ar5211_xmit.c (ar5211ResetTxQueue): /* NB: chan can be NULL during attach */ followed by if (chan && IEEE80211_IS_CHAN_B(chan)) β€” chan used nowhere else.
  • ar5212_xmit.c (ar5212ResetTxQueue): identical guard, identical comment.

The 5210 HAL missed the guard, and unlike its siblings it uses chan for the whole IFS programming branch (turbo vs normal), so the deref executes on every DATA resetTxQueue call with a NULL channel.

Reachability (why latent)

In-tree order: ath_attach runs ath_hal_reset (sets ah_curchan) before ath_txq_setup calls ath_hal_resettxqueue (if_ath.c:4125-4130), and the only other caller (ath_txq_update) runs after channel changes. So no in-tree path reaches the deref with chan == AH_NULL; a driver variant (or future caller) that sets up/resets a DATA queue before the first channel reset takes a NULL-deref kernel panic. Same "latent, caller-order-protected" class as the DF-4519/4712/4733 family.

Reproduce

Hardware-gated (no Atheros NIC on the guest). Source-confirmed divergence plus fix build-validation:

scp fix.diff guest:/tmp/df4734.diff
vm.sh run_root 'cd /usr/src && patch -p1 < /tmp/df4734.diff && make nativekernel KERNCONF=X86_64_GENERIC'
# expected: BUILD_RC=0, ar5210_xmit.c compiled -Werror (build.log)

Fix

fix.diff: if (chan != AH_NULL && IEEE80211_IS_CHAN_TURBO(chan)) with the siblings' comment β€” NULL channel takes the normal-mode defaults, matching ar5211/ar5212 semantics (they fall back to INIT_CWMIN when chan is NULL).

VERDICT.md
↓ download raw

VERDICT β€” DF-4734

Finding

ar5210ResetTxQueue caches AH_PRIVATE(ah)->ah_curchan (ar5210_xmit.c:157) and dereferences it unconditionally in IEEE80211_IS_CHAN_TURBO(chan) (line 180) for every DATA-queue reset. IEEE80211_IS_CHAN_TURBO is a raw (_c)->ic_flags deref (sys/netproto/802_11/_ieee80211.h:268). ah_curchan is NULL from attach until the first ar5210Reset. Both sibling HALs carry an explicit guard with the comment "NB: chan can be NULL during attach" and use chan nowhere unguarded:

  • ar5211_xmit.c ar5211ResetTxQueue: if (chan && IEEE80211_IS_CHAN_B(chan))
  • ar5212_xmit.c ar5212ResetTxQueue: if (chan && IEEE80211_IS_CHAN_B(chan))

(verified by extracting both functions β€” chan appears only inside the guard). The 5210 HAL missed it, and its usage is broader (the whole turbo/normal IFS programming branch), so a DATA resetTxQueue before the first channel reset is a NULL-deref kernel panic.

Reachability (latent)

In-tree call order protects: ath_attach performs ath_hal_reset (which sets ah_curchan) before ath_txq_setup β†’ ath_hal_resettxqueue (if_ath.c:4125-4130); the other caller (ath_txq_update) runs on channel changes. No in-tree path reaches the deref with NULL. Same caller-order-protected latent class as DF-4519/4712/4733. Hardware-gated on the guest (no Atheros NIC, verified).

Verification

  • Runtime: not possible on the guest (no ar5210 hardware; the HAL is inert).
  • Fix build-validated: fix.diff (chan != AH_NULL && + sibling comment) applied to guest /usr/src; single make nativekernel KERNCONF=X86_64_GENERIC build together with DF-4733/DF-4735: BUILD_RC=0, -Werror, ar5210_xmit.c compiled (build.log).

Bottom line

Certain, precisely-scoped cross-version divergence (both siblings guard + annotate; 5210 forgot). Low severity latent NULL-deref panic; one-line fix mirroring siblings, build-validated.

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

fixed
baseline no→ patch + rebuild →patched clean

build-only validation (runtime hardware-gated): fix.diff applied; nativekernel BUILD_RC=0 -Werror (build.log:7129 ar5210_xmit.c); kernel installed and booted clean. NULL-chan path not exercisable on the guest (no ath hardware).

findings/poc/DF-4734/build.log (RC=0; ar5210_xmit.c -Werror at :7129); fix.diff
↓ fix.diffper-fix-DF-4734

Confirmed kernel references

Detail

Exploit chain

[]

Evidence (decisive lines)

findings/poc/DF-4734/VERDICT.md (divergence proof: sibling guards quoted); build.log (RC=0; ar5210_xmit.c -Werror at :7129); fix.diff (one-line NULL guard mirroring siblings); env.txt

PoC changes

n/a (no runtime PoC attempted; hardware-gated per DF-4519/DF-4712 protocol)

Verified recommended fix

Guard the deref: 'if (chan != AH_NULL && IEEE80211_IS_CHAN_TURBO(chan))' - one line mirroring the ar5211/ar5212 NULL-chan guards.

Verdict

untested (runtime): ar5210ResetTxQueue caches AH_PRIVATE(ah)->ah_curchan (ar5210_xmit.c:157) and dereferences it unconditionally in IEEE80211_IS_CHAN_TURBO(chan) (:180; macro is a raw ->ic_flags deref, _ieee80211.h:268). ah_curchan is NULL from attach until the first ar5210Reset. Both sibling HALs guard exactly this with 'if (chan && ...)' and the comment 'NB: chan can be NULL during attach' (ar5211_xmit.c ar5211ResetTxQueue; ar5212_xmit.c ar5212ResetTxQueue - verified: chan appears only inside the guard in both); ar5210 missed it and its usage is broader (the whole turbo/normal IFS branch). In-tree call order protects (ath_attach resets before ath_txq_setup, if_ath.c:4125-4130) - latent. No Atheros NIC on the guest, runtime not observable. Fix build-validated: nativekernel BUILD_RC=0 -Werror (build.log:7129), kernel installed and booted clean.