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

RANN frame overwrites shared global ieee80211_hwmp_rannint without lock: remote timer DoS

Summary

hwmp_recv_rann(:1971) ieee80211_hwmp_rannint=rann->rann_interval from received frame, no lock (XXX: mtx lock? comment). Shared across all vaps, drives callout period(:841). Remote: RANN with rann_interval=0 -> callout tight loop CPU saturation. Data race vs sysctl handler.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0352 Β· 6 files
FileTypeDescriptionSize
fix.diff suggested-fix atomic_swap_int + sanity clamp on rann_interval write 956 B view raw
VERDICT.md verdict source-trace confirmation, why not reachable on guest 3.5 KB ↓ raw
env.txt environment guest has no WiFi HW / no wlan module loaded 302 B view raw
README.md readme human reproduce doc 1.1 KB ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme human reproduce doc
↓ download raw

DF-0352 PoC β€” RANN overwrites shared ieee80211_hwmp_rannint (no lock)

Status: LATENT β€” code-level bug confirmed, NOT reachable on audit guest. Severity: Medium (downgraded from "DoS" because guest lacks WiFi HW)

Bug

sys/netproto/802_11/wlan/ieee80211_hwmp.c:1971 writes the on-wire rann->rann_interval directly into the file-scope global ieee80211_hwmp_rannint (used by every vap's RANN callout) with no lock (the comment even says XXX: mtx lock?). A remote on-link attacker can forge a RANN with rann_interval=0 to pin the callout at hard-clock frequency (CPU-saturation DoS), and the unlocked store races sysctl reads of the same global.

Why not testable here

The audit guest has no WiFi hardware and no wlan/mesh module loaded. hwmp_recv_rann is dead code at runtime on this guest; an unprivileged user cannot create a vap, and kldload is root-only. This is the "genuinely not reachable on this kernel" category with a real latent bug β€” see VERDICT.md for the full source trace.

Files

  • fix.diff β€” atomic write + sanity lower-bound
  • VERDICT.md β€” full narrative
  • env.txt β€” guest environment
VERDICT.md verdict source-trace confirmation, why not reachable on guest
↓ download raw

DF-0352 β€” RANN frame overwrites shared global ieee80211_hwmp_rannint (no lock)

Verdict

LATENT β€” code-level defect confirmed, NOT reachable on this guest.

The bug is real by source inspection. The audit guest has no WiFi hardware and no wlan/mesh module loaded, so the receive path that writes the shared global (hwmp_recv_rann) is dead code at runtime here. An unprivileged local user cannot create a WiFi vap, inject a frame, or load a WiFi driver module β€” all are root-only operations on this guest. This is the "genuinely not reachable on this kernel" category with a real latent bug.

Bug mechanism (source trace)

File: sys/netproto/802_11/wlan/ieee80211_hwmp.c.

  • Line 183: static int ieee80211_hwmp_rannint = -1; β€” file-scope global shared across all vaps in the system.
  • Lines 184–186: exposed as a writable sysctl (net.wlan_hwmp.rannint), with no internal locking.
  • Line 220: initialized to msecs_to_ticks(1*1000) at module load.
  • Line 841: callout_reset(&hs->hs_roottimer, ieee80211_hwmp_rannint, ...) β€” drives the periodic root-announcement callout period for every RANN-rooted vap, using the shared global.
  • Line 914: same global is read when sending a RANN (rann.rann_interval = ieee80211_hwmp_rannint).
  • Line 1971 (the bug): inside hwmp_recv_rann, on receiving ANY RANN frame from the wire: c ieee80211_hwmp_rannint = rann->rann_interval; /* XXX: mtx lock? */ The on-the-wire rann_interval field (attacker-controlled) is written directly into the shared global with no lock (the XXX: mtx lock? comment admits the omission).

Effects if reachable (realistic threat model)

On a host with a WiFi NIC in mesh mode + HWMP root mode: - A remote on-link attacker sends a forged RANN with rann_interval=0 (or 1 tick). The victim's RANN callout is reset to that interval, producing a tight-loop callout firing every tick β€” CPU saturation on the callout's CPU. - Concurrent sysctl reads / vap switches race the unlocked write β€” torn reads, possibly bogus callout_reset args. - The attacker controls rann_interval (16-bit), but the effect is bounded to "drive callout at attacker-chosen rate"; no direct memory corruption in this specific write.

So the realistic impact ceiling is remote DoS via CPU saturation + lockless-write race. No memory-corruption primitive.

Why not testable on this guest

$ ssh dfbsd-maxx 'ifconfig -l'
vtnet0 lo0
$ ssh dfbsd 'kldstat | grep mesh'   # (empty)
$ ssh dfbsd 'pciconf -l | grep -i wireless'   # (empty)

No WiFi hardware is presented to the guest. wlan.ko exists on disk but is not loaded (and kldload is root-only, hence cannot be part of an unprivileged chain). A vap cannot be created without a WiFi driver instance, which itself requires real WiFi hardware or a software-defined radio β€” neither available here.

The bug requires: WiFi NIC driver attached + vap in HWMP/RANN root mode + receiving a forged RANN. None of these can be set up by an unprivileged user, and none are present by default on the audit guest.

Take hs->hs_lock (or the mesh state lock) around the write at line 1971, AND validate rann->rann_interval against a sane minimum (e.g. reject 0 / very-small values to prevent the callout-tightening DoS). See fix.diff.

Files in this folder

  • fix.diff β€” adds a lock around the write + a sanity lower-bound
  • VERDICT.md β€” this file
  • manifest.json
  • env.txt β€” guest environment confirming no WiFi HW

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. RANN frame overwrites global ieee80211_hwmp_rannint unlocked. No WiFi HW.