⬢ DragonFlyBSD Kernel Audit
DF-3470 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-3470",
  "status": "inconclusive",
  "reproduced": 0,
  "impact": "panic",
  "confidence": "likely",
  "verdict": "net80211 rate-control registry hands raw module-resident pointers to vaps (ratectl.c:152) with no in-use accounting in ieee80211_ratectl_unregister (ratectl.c:66-71); the POLICY_MODULE nrefs/EBUSY unload guard exists (ieee80211_dragonfly.h:316-336) but amrr/rssadapt/none never increment nrefs (amrr.c:77 single occurrence; wep.c:108/scan_sta.c:178 show the intended pattern), so kldunload of an in-use algorithm unmaps the module while every vap->iv_rate dangles - the next ir_* indirect call (unprivileged world-readable rate_stats sysctl read, any TX, association, vap detach) executes unmapped text => panic / execute-after-unmap. Runtime reproduction impossible on this guest: wlan_amrr is compiled into the kernel (kldunload: cant find file) and no 802.11 hardware exists to attach a vap; mechanism proven from source (analysis.txt).",
  "exploit_chain": "root: kldunload wlan_amrr while any vap is attached (registry shows no users; unload succeeds) -> module text unmapped, vap->iv_rate dangling -> unprivileged: sysctl net.wlan.0.rate_stats (CTLFLAG_RD, no privilege) or any packet TX -> indirect call through unmapped function-pointer table -> kernel panic; if the vacated module address range is reoccupied by a later kldload, call target becomes attacker-influenced module content.",
  "evidence": [
    "analysis.txt (nrefs occurrence counts across netproto/802_11; the unused guard macro; the dangling consumer chain; guest kldstat/kldunload facts)",
    "VERDICT.md"
  ],
  "kernel_refs": [
    "sys/netproto/802_11/wlan/ieee80211_ratectl.c:58",
    "sys/netproto/802_11/wlan/ieee80211_ratectl.c:66",
    "sys/netproto/802_11/wlan/ieee80211_ratectl.c:152",
    "sys/netproto/802_11/wlan/ieee80211_ratectl.c:127",
    "sys/netproto/802_11/ieee80211_dragonfly.h:316",
    "sys/netproto/802_11/wlan/ieee80211_amrr.c:77",
    "sys/netproto/802_11/wlan/ieee80211_rssadapt.c:97",
    "sys/netproto/802_11/wlan/ieee80211_ratectl_none.c:96",
    "sys/netproto/802_11/wlan_wep/ieee80211_crypto_wep.c:108"
  ],
  "poc_changes": "No seed PoC; this is a source-proven lifetime defect. A runtime trigger would require (a) wlan_amrr as a loadable module and (b) one attached vap - neither exists on the audit guest.",
  "attempts": 2,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 120,
  "guest_dirty": 0,
  "build_cmd": "n/a (no buildable trigger on this guest: wlan_amrr compiled-in, no 802.11 hardware)",
  "run_cmd": "n/a (would be: kldload wlan_amrr; create vap via wifi driver; kldunload wlan_amrr; sysctl net.wlan.0.rate_stats)",
  "code_hash": "",
  "notes": "Same defect family as DF-3449 (dummynet unload stale hooks, Medium). amrr is the DEFAULT ratectl (ratectl.c:121-122) so every vap is exposed on module-built systems. Sibling modules that arm the guard correctly: wlan_wep (crypto_wep.c:108/118-119), scan_sta (178/195-196), tkip. Guest verified facts: kldstat -v lists wlan_amrr under kernel id 1; kldunload wlan_amrr -> cant find file; grep nrefs in guest /usr/src amrr.c -> 1 occurrence (declaration only).",
  "recommended_fix": "Arm the nrefs guard in amrr/rssadapt/none (mirror wep: nrefs++ in ir_init, nrefs-- in ir_deinit) or move per-type user accounting into the registry so ieee80211_ratectl_unregister refuses while vaps reference the algorithm.",
  "fix_status": "not_testable",
  "fix_kernel_uname": "",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Fix validation not possible on this guest: the unload precondition (loadable wlan_amrr + attached vap) cannot be constructed (algorithm compiled into the kernel; no 802.11 hardware).",
  "fix_evidence": "n/a"
}