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

ieee80211_create_wds leaks the find_node_locked reference in the 'station in use' branch: permanent node refcount inflation, un-reclaimable station table entries

Summary

ieee80211_find_node_locked returns the node with +1 reference. ieee80211_create_wds consumes it only in the success branch; the ni->ni_wdsvap != NULL 'in use' branch just DPRINTFs and drops the reference on the floor - the station node's refcount is permanently inflated and node_reclaim can never free it. Two WDS vaps sharing one iv_des_bssid (daemon race/misconfig) leak one reference per losing INIT->RUN transition, and alternating up/down re-arms the branch (INIT's node_table_reset second pass clears ni_wdsvap). FreeBSD HEAD identical - inherited. Privileged local plumbing; slow table pollution; no memory unsafety. Fix: ieee80211_free_node(ni) in the branch.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3430 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.8 KB ↓ raw
trigger_create_wds_refleak.c β€” 2.0 KB view raw
fix.diff β€” 685 B view raw
verdict.json β€” 2.1 KB view raw
manifest.json β€” 766 B view raw

DF-3430 β€” ieee80211_create_wds "in use" branch leaks node reference

Severity: Low | Analysis pack (Phase V skipped for Low per audit policy)

The bug

ieee80211_find_node_locked() returns the node with a +1 reference (sys/netproto/802_11/wlan/ieee80211_node.c:1391, ieee80211_ref_node). ieee80211_create_wds (sys/netproto/802_11/wlan/ieee80211_wds.c:149-188) consumes that reference only in the success branch (vap->iv_bss = ni, :181). In the ni->ni_wdsvap != NULL branch ("station in use", :163-175) it merely DPRINTFs β€” the reference is never dropped: permanent +1 refcount inflation of the station node.

Consequences: the node can never be reclaimed by node_reclaim (refcount never reaches 0); it stays in ic_sta (and its hash bucket) until reboot. Each leak also keeps the node's key state, rates etc. resident. Repeated triggering (see below) pollutes the table unboundedly.

Trigger

Two WDS vaps with the same iv_des_bssid pointing at one associated station (management daemon races / misconfig): each INIT→RUN transition of the vap that loses the hookup leaks one reference; alternating up/down cycles of the two vaps repeats it (down clears ni_wdsvap via ieee80211_node.c:2004-2010's second pass, re-arming the "in use" branch).

Upstream

FreeBSD HEAD sys/net80211/ieee80211_wds.c:161-167 β€” identical branch, identical leak (verified 2026-09-12). Inherited defect.

Fix

Drop the reference in the "in use" branch: ieee80211_free_node(ni); (inside the lock is fine β€” free_node handles it, as used elsewhere under NODE_LOCK; or defer after unlock like obss). See fix.diff.

Verdict

untested β€” Low severity (requires root to plumb the vaps; slow exhaustion, no memory unsafety), Phase V skipped per policy; and the QEMU guest has no 802.11 radio to build vaps on anyway.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

one-line refcount fix authored; not validated (Low severity, no radio hardware on guest)

fix.diff
↓ fix.diffper-fix-DF-3430

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md (branch analysis); trigger_create_wds_refleak.c (annotated); fix.diff

PoC changes

No seed; analysis-only pack (Low severity => Phase V skipped by policy).

Verified recommended fix

ieee80211_free_node(ni) in the 'in use' branch of ieee80211_create_wds; see fix.diff.

Verdict

ieee80211_create_wds's 'station already in use' branch (ieee80211_wds.c:163-175) never drops the +1 node reference acquired by ieee80211_find_node_locked (ieee80211_node.c:1391): the station node's refcount is permanently inflated and the node can never be reclaimed, polluting ic_sta until reboot. Repeatable via two WDS vaps sharing one iv_des_bssid (alternating INIT->RUN transitions re-arm the branch because node_table_reset's second pass clears ni_wdsvap on INIT). Low severity: privileged local plumbing required, slow resource exhaustion, no memory unsafety. FreeBSD HEAD has the identical branch (ieee80211_wds.c:161-167) - inherited. Phase V skipped per audit policy for Low findings (and the QEMU guest has no 802.11 radio).