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

ieee80211_ht_wds_init never assigns tap->txa_ni: guaranteed NULL-deref panic in addba_timeout/ampdu_tx_stop/bar_timeout for legacy WDS nodes with HT

Summary

struct ieee80211_tx_ampdu.txa_ni has exactly one writer in the tree - ieee80211_ht_node_init (:1084). Legacy WDS peer nodes are instead initialized by ieee80211_ht_wds_init, which sets txa_tid but not txa_ni; nodes are kmalloc'd M_ZERO so txa_ni is NULL forever while the node is marked NODE_HT|NODE_AMPDU, arming the TX aggregation path. addba_timeout, ampdu_tx_stop and bar_timeout all deref tap->txa_ni->ni_ic unconditionally. Reachable config: HT radio + 'wlanmode wds wlanbssid <peer> wdslegacy' vap with 'ifconfig <wds> ht' (IOC_HTCONF has no opmode restriction); >=64 pps BE traffic -> ADDBA request; peer silence (dead link or attacker-controlled peer) -> 250ms later addba_timeout panics the kernel. REPRODUCED on stock INVARIRICS guest (fw2.c software radio + wdslegacy vap + paced traffic): 'Fatal trap 12 ... fault VA = 0x8 / Stopped at addba_timeout+0x6: movq 0x8(%rdi),%rax' within ~250ms. Remote-adjacent-triggerable DoS of any 11n legacy-WDS link; fixed NULL+8 read, no exploitation primitive. Fix validated (txa_ni = ni in the WDS loop): PoC no longer panics, guest survives repeated timeouts.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3312 Β· 15 files
FileTypeDescriptionSize
fw2.c β€” 9.9 KB view raw
Makefile β€” 150 B ↓ download
build.sh β€” 143 B view raw
run.sh β€” 111 B view raw
fixcheck.sh β€” 1.6 KB view raw
build.log β€” 5.6 KB view raw
run.log β€” 3.0 KB view raw
run.2.log β€” 3.0 KB view raw
panic.txt β€” 2.1 KB view raw
patched_run.txt β€” 1.2 KB view raw
fix.log β€” 1.4 KB view raw
fix.diff β€” 422 B view raw
env.txt β€” 258 B view raw
verdict.json β€” 4.3 KB view raw
manifest.json β€” 1.3 KB view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

One-line fix.diff applied to guest /usr/src, nativekernel rebuilt+installed (#1 Sep 10 14:40:48), rebooted, exact baseline PoC re-run: tap->txa_ni now 0xfffff80118743400, ADDBA request armed (txa_flags=0xb) and timed out repeatedly with no trap; guest stayed up (dmesg fatal-trap count 0). Bad behavior gone.

fix.log, patched_run.txt
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Thu Sep 10 14:40:48 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Exploit chain

HT radio + legacy WDS vap ('wlanmode wds wlanbssid wdslegacy') + 'ifconfig ht' -> RUN -> ieee80211_create_wds legacy branch -> ieee80211_node_create_wds -> ieee80211_ht_wds_init (txa_ni stays NULL, node M_ZERO) -> unicast traffic >= mintraffic threshold -> ieee80211_ampdu_request -> addba_start_timeout (250 ms) -> peer silence -> addba_timeout: ni = tap->txa_ni (NULL); ic = ni->ni_ic -> Fatal trap 12, fault va 0x8, Stopped at addba_timeout+0x6. Teardown paths ampdu_tx_stop:1731 and bar_timeout:2282 deref the same NULL.

Evidence (decisive lines)

["run.log: decisive baseline run β€” 'tap->txa_ni = 0', txa_flags=0xb (ADDBA pending), then 'Fatal trap 12 ... fault virtual address = 0x8 ... Stopped at addba_timeout+0x6: movq 0x8(%rdi),%rax', current process Idle (callout)", 'panic.txt: full serial-console capture of the trap', "patched_run.txt: fixed-kernel rerun β€” 'tap->txa_ni = 0xfffff80118743400', same traffic + ADDBA pending, no trap", 'fix.log: patch apply + nativekernel build/install + post-PoC status (guest up, 0 fatal traps)', 'fw2.c: software-radio KLD (HT 11ng fake radio, WDS-capable, paced-traffic sysctl kern.fw2_fire)']

PoC changes

Two harness fixes vs first draft: (1) take one ieee80211_ref_node per frame (ieee80211_vap_pkt_send_dest consumes a node ref per call -- first run UAF'd in ieee80211_tx_complete, PoC bug not finding bug); (2) assign ic_raw_xmit/ic_transmit AFTER ieee80211_ifattach (proto_attach overwrites them with null_raw_xmit, cosmetic). Finding-side PoC seed: none existed (pass-2 new finding); trigger sequence matches analysis exactly.

Verified recommended fix

Assign tap->txa_ni = ni in ieee80211_ht_wds_init (mirrors ieee80211_ht_node_init:1084); see fix.diff -- validated by kernel rebuild.

Verdict

Legacy WDS peer nodes initialized by ieee80211_ht_wds_init get NODE_HT|NODE_AMPDU but tap->txa_ni is never assigned (only ieee80211_ht_node_init:1084 assigns it); with HT re-enabled on the WDS vap (HTCONF ioctl, no opmode restriction) and >=64 pps BE traffic the stack sends an ADDBA request, and 250 ms of peer silence makes addba_timeout() deref NULL txa_ni -> deterministic page fault at va 0x8 in softclock context. Reproduced twice on the stock INVARIANTS guest; one-line fix (txa_ni = ni in ieee80211_ht_wds_init) validated by nativekernel rebuild: identical PoC no longer panics, tap->txa_ni non-NULL, guest stays up. Remote-peer-triggerable DoS (panic) on WDS+11n deployments; no exploitation primitive (fixed NULL+8 read).