⬢ DragonFlyBSD Kernel Audit
DF-3429 / trigger_stageq_leak.c
← back to finding ↓ download raw
/* DF-3429 — DWDS discovery frames staged on ic_stageq are LEAKED when they
 *           age out: ieee80211_node_timeout() drops the mbuf list returned
 *           by ieee80211_ageq_age() on the floor.
 *
 * Producer (this file's function):
 *   sys/netproto/802_11/wlan/ieee80211_wds.c:321-338
 *   ieee80211_dwds_discover(ni, m):
 *       m->m_pkthdr.rcvif = (void *)(uintptr_t) ieee80211_mac_hash(...);
 *       (void) ieee80211_ageq_append(&ic->ic_stageq, m,
 *           ((ni->ni_intval * ic->ic_lintval) << 2) / 1024);   // finite TTL
 *       ieee80211_notify_wds_discover(ni);
 *
 * Reachability: any AUTHORIZED station of an AP vap can reach this by
 * sending a 4-address data frame while ni->ni_wdsvap == NULL
 * (sys/netproto/802_11/wlan/ieee80211_hostap.c:654-667 gates on
 * authorization, then calls ieee80211_dwds_discover).  On an open/PSK
 * network, association is all an attacker needs.
 *
 * Sink (the leak):
 *   sys/netproto/802_11/wlan/ieee80211_node.c:2255
 *   ieee80211_node_timeout(void *arg):
 *       ...
 *       ieee80211_ageq_age(&ic->ic_stageq, IEEE80211_INACT_WAIT);  // <-- RETURN IGNORED
 *
 *   ieee80211_ageq_age() REMOVES expired frames from the queue and returns
 *   them as an m_nextpkt-joined list for the caller to free
 *   (sys/netproto/802_11/wlan/ieee80211_ageq.c:162-185).  The bare call
 *   discards the list: every expired stageq frame leaks its mbuf + cluster
 *   (~2KB+ per frame).
 *
 * Normal operation pulls staged frames via wds_flush() (wds.c:102-122,
 * ieee80211_ageq_remove) once a WDS vap is plumbed; the leak hits whenever
 * discovery events are NOT serviced (no hostapd DWDS plumbing, daemon
 * restart, race) - then every staged frame becomes a permanent kernel
 * memory leak after its TTL.  The queue itself is bounded
 * (ic_stageq aq_maxlen = ic_max_keyix * 8, ieee80211_node.c:113) but aging
 * empties it, so a sustained 4-addr stream = sustained leak
 * (attacker-rate x ~2KB/s) -> kernel heap exhaustion -> OOM/panic.
 *
 * Upstream: FreeBSD's ieee80211_node_timeout() has the identical bare call
 * (verified against FreeBSD HEAD sys/net80211/ieee80211_node.c:2568) -
 * inherited defect.
 *
 * Not runtime-testable in the QEMU audit guest: no 802.11 radio hardware
 * attaches (kldload if_iwm/if_run find no device; ifconfig -C has no
 * wlandev), so no ieee80211com/vap/node exists to exercise the path.
 */
int main(void) { return 1; }