{
  "finding_id": "DF-3748",
  "status": "not_reproduced",
  "reproduced": 0,
  "impact": "none",
  "confidence": "certain",
  "verdict": "Source-certain remote kernel OOB write: in STA mode net80211 stores the AP-supplied 16-bit AID verbatim (ieee80211_sta.c:1688, unvalidated), rt2860_newassoc truncates it into a uint8_t wcid and writes wcid2ni[wcid] (rt2860.c:885-887) into a 254-entry array (rt2860var.h:148); an attacker AP assigning AID 254 overwrites txq[0].txd with a live ieee80211_node pointer, and every later AC-BE transmit then writes 16-byte TX descriptors across the node object and adjacent heap (rt2860.c:1715-1741, cur up to 63) -- unauthenticated remote heap corruption with crash near-certain and a plausible grooming path to RCE. Same truncation also reaches the TX-status drain read at rt2860.c:1136-1140 (only wcid==0xff filtered). Not reproduced only because the QEMU audit guest has no Ralink NIC (pciconf shows none; env.txt); if_ral.ko is built, present in /boot/kernel and one kldload away on any host with the hardware.",
  "exploit_chain": "victim STA associates to attacker AP -> AP returns AID 254 (or 0xC0FE/510/0xC1FE...) in assoc response -> ieee80211_sta.c:1688 sets ni_associd=254 -> S_RUN transition calls ic_newassoc (sta.c:453) -> rt2860_newassoc:887 sc->wcid2ni[254]=ni aliases txq[0].txd -> victim sends/receives first data frame on ring 0 -> rt2860_tx writes txd at node+cur*16 for cur 0..63 (>=1KB 16-byte-stride semi-controlled writes: lengths attacker-sized, sdp0 = physical TXWI addr, flags) -> node ni_vap/ni_ic/refcnt/key pointers corrupted -> panic or, with heap grooming of M_80211_NODE neighbours, controlled corruption (undemonstrated, needs hardware).",
  "evidence": "VERDICT.md (full line-by-line chain), README.md (trigger recipe + expected observation), env.txt (guest hardware absence), fix.diff (applies clean via git apply --check)",
  "kernel_refs": [
    "sys/dev/netif/ral/rt2860.c:885",
    "sys/dev/netif/ral/rt2860.c:887",
    "sys/dev/netif/ral/rt2860.c:1587",
    "sys/dev/netif/ral/rt2860.c:1136",
    "sys/dev/netif/ral/rt2860.c:1140",
    "sys/dev/netif/ral/rt2860.c:1715",
    "sys/dev/netif/ral/rt2860var.h:148",
    "sys/dev/netif/ral/rt2860var.h:28",
    "sys/netproto/802_11/wlan/ieee80211_sta.c:1688",
    "sys/netproto/802_11/wlan/ieee80211_sta.c:453",
    "sys/netproto/802_11/ieee80211.h:1314"
  ],
  "poc_changes": "No PoC source produced: trigger requires 802.11 radio + Ralink NIC (rogue AP assigns AID 254 in assoc response). Guest verified to lack the device; pack documents the hardware recipe instead.",
  "attempts": 1,
  "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": 12,
  "guest_dirty": 0,
  "build_cmd": "",
  "run_cmd": "dfbsd-qemu/vm.sh run_root pciconf -lv | grep ralink; ifconfig -l",
  "code_hash": "",
  "notes": "Pass-2 finding (file was pass-1 clean, no prior findings). Guest module check: kldstat -v lists pci/ral id 104; /boot/kernel/if_ral.ko present (1.59MB); no ralink PCI device -> driver cannot attach. Hostap mode is safe from this specific write (AID allocator caps at 253 via iv_max_aid=min(2007,254) + aid<max loop, wlan/ieee80211_node.c:2539); the hole is STA-mode-only because there the AID is attacker-supplied. Recommended companion fix upstream: validate AID in ieee80211_sta.c assoc-response parsing.",
  "recommended_fix": "Size wcid2ni[] for the full 8-bit WCID space (RT2860_WCID_MAPSZ 256, rt2860var.h) and filter wcid >= RT2860_WCID_MAX in rt2860_drain_stats_fifo; optionally validate the AP-provided AID in ieee80211_sta.c:1688.",
  "fix_status": "not_testable",
  "fix_baseline_reproduced": 0,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff is a minimal, compile-consistent change (array width + drain guard) that removes both the OOB write and the OOB read sink of the truncation; it cannot be runtime-validated on the audit guest because baseline itself needs the Ralink NIC.",
  "fix_evidence": "fix.diff (git apply --check passes against pristine sys/); no fix_build.log/fix_run.log possible without hardware."
}