⬢ DragonFlyBSD Kernel Audit
DF-3364 / verdict.json
← back to finding ↓ download raw
{
 "finding_id": "DF-3364",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "leak",
 "confidence": "certain",
 "verdict": "tdma_isfull() (ieee80211_scan_sta.c:965) indexes tdma_inuse[slot>>3] for slot up to the attacker-controlled slotcnt-1 (255) although the array is one byte inside an IE that can be as short as 6 bytes; with the TDMA IE last in a 64-byte blob the kernel read tdma_inuse[31] = IE byte 44 = 30 bytes past the heap allocation, and that bytes content flipped the candidate decision: 0x00 groom -> clear at slot 254 -> fail=0x0 -> C ACCEPTED; 0xff groom -> FULL -> fail=0x1000 -> C REJECTED. Proven with kprintf instrumentation inside the real tdma_isfull/match_bss on an otherwise-stock kernel (probe-only patch); gcc -Warray-bounds independently flags the same index as out-of-bounds. Post-fix (fix.diff kernel #2) all short TDMA IEs are rejected as malformed and the full-length valid IE (test 17) is accepted.",
 "exploit_chain": "unauthenticated beacon to an AHDEMO+TDMA vap (root-created, driver with C_TDMA) -> short TDMA-OUI vendor IE saved verbatim in the blob -> ies_expand exposes tdma_ie (len>=4 only) -> match_bss reads tdma_version/slot past minimal IEs -> tdma_isfull scans 248 attacker-directed bitmap bytes beyond the allocation -> OOB heap bytes decide candidate acceptance. Oracle-grade; no write primitive.",
 "evidence": [
  "run.log: DBG isfull: ... slotcnt=255 inuse[0]=ff inuse[31]=00 / clear at slot 254 (byte +44) -> ACCEPTED vs inuse[31]=ff / FULL / fail=0x1000 -> REJECTED",
  "../DF-3363/fix_validation.log: post-fix TDMA matrix (17 accepted, 11/12/14 rejected as malformed)",
  "build.log excerpt in VERDICT: gcc -Werror=array-bounds rejects tdma_inuse[31] as above array bounds"
 ],
 "kernel_refs": [
  "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:959",
  "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:965",
  "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:1016",
  "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:1023",
  "sys/netproto/802_11/ieee80211_input.h:118",
  "sys/netproto/802_11/wlan/ieee80211_node.c:1001"
 ],
 "poc_changes": "same harness as DF-3363 plus: TDMA vap must be created via wlanmode tdma (ifconfig clone flag IEEE80211_CLONE_TDMA); Cs rates must include 11 Mbps (the TDMA vap pins ucastrate=2*11 via TDMA_TXRATE_11B_DEFAULT); IEEE80211_SCAN_NOJOIN used so the selection verdict is observable without driving the TDMA join state machine (which hangs on the fake radio); the flaky early runs were background-scan se_notseen bumps, fixed by re-injecting C right before scan_end.",
 "attempts": 8,
 "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1 Fri Sep 11 12:28:18 UTC 2026 (instrumented, logic stock) for baseline; #2 fixed kernel for validation",
 "runtime_sec": 3600,
 "guest_dirty": 0,
 "build_cmd": "sh findings/poc/DF-3364/build.sh",
 "run_cmd": "ifconfig wlan create wlandev fws0 wlanmode tdma up; sysctl kern.fwscan_test=11|12|14|17",
 "code_hash": "54859c80a66d25ec2d056f0dec66495fd2fc3b3c665fe93c13f0283adf0f847f",
 "notes": "Reachability needs AHDEMO+TDMA (config-dependent) - hence Low severity despite certain code defect. match_bss also dereferences vap->iv_tdma without a NULL check when iv_caps has C_TDMA (driver forgot tdma_vattach) - covered by the same fix.diff (ts==NULL guard).",
 "recommended_fix": "require tdma_len >= sizeof(struct ieee80211_tdma_param)-2 before reading fields, clamp slotcnt to 8*nitems(tdma_inuse); see fix.diff.",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #2 Fri Sep 11 12:44:59 UTC 2026",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "with fix.diff, the 12-byte TDMA IE variants (11/12/14) are all rejected (MATCH_TDMA_NOIE) regardless of heap groom; the full-length valid IE (17) is accepted. No OOB-dependent behavior remains.",
 "fix_evidence": "findings/poc/DF-3363/fix_validation.log"
}