DF-3365 / verdict.json
{ "finding_id": "DF-3365", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Three additional unlocked-use-after-unlock windows exist beyond the known DF-0395/0396: (a) sta_adds PICK1ST match_bss tail at ieee80211_scan_sta.c:403 runs after TABLE_UNLOCK at :397 on every beacon of a PICK1ST scan; (b) select_bss returns selbs after unlocking (:1210) and sta_pick_bss/adhoc_pick_bss/mesh_pick_bss/sta_roam_check dereference it and pass &selbs->base to ieee80211_sta_join which copies the entry and its se_ies blob wholesale; (c) roam/assoc callbacks via sta_lookup. Lock analysis proves the windows are live cross-thread on DragonFly: rx runs under wlan_global_serializer while node_timeout->adhoc_age frees and the taskqueue scan state machine run under the table/ic locks only. A 180s two-thread race (171k+ injections vs serializer-free flushes, INVARIANTS + use_weird_array junk-fill) produced no panic or visible corruption - the sub-microsecond window and mapped-slab reads make runtime demonstration impractical. Verdict: genuine UAF-read race, not reproduced.", "exploit_chain": "", "evidence": [ "run.log: race attempts (150000 + 21800 injections survived, guest stable)", "VERDICT.md lock analysis: wlan_global_serializer (ieee80211.c:720) vs table-lock-only callout/taskq contexts (ieee80211_node.c:2239, ieee80211_scan_sw.c:624)", "fix.diff hunk: table lock held across the PICK1ST match_bss tail (validated in the DF-3363 fix kernel build - no regressions across the full PoC matrix)" ], "kernel_refs": [ "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:397", "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:403", "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:1210", "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:1269", "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:1353", "sys/netproto/802_11/wlan/ieee80211_scan_sta.c:1669", "sys/netproto/802_11/wlan/ieee80211_node.c:832", "sys/netproto/802_11/wlan/ieee80211_node.c:2239" ], "poc_changes": "race threads built into the fwscan harness: injector under wlan_serialize (rx-path discipline) and flusher without the serializer (node_timeout discipline); PICK1ST kept armed by injecting rssi=1 beacons that always fail match so GOTPICK never sets.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1 Fri Sep 11 12:28:18 UTC 2026 x86_64", "runtime_sec": 300, "guest_dirty": 0, "build_cmd": "sh findings/poc/DF-3365/build.sh", "run_cmd": "ifconfig wlan create wlandev fws0 wlanmode sta up; sysctl debug.use_weird_array=1; sysctl kern.fwscan_race=120", "code_hash": "54859c80a66d25ec2d056f0dec66495fd2fc3b3c665fe93c13f0283adf0f847f", "notes": "Same systemic root cause as DF-0395/0396 (no refcount on sta_entry) - new sites, wider blast radius (sta_join blob copy). The PICK1ST window is closed by the validated fix.diff; full fix = entry refcounting.", "recommended_fix": "hold the table lock across the PICK1ST match_bss tail (done in fix.diff); add reference counting to struct sta_entry for the select_bss/join consumers.", "fix_status": "inconclusive", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #2 Fri Sep 11 12:44:59 UTC 2026", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "the PICK1ST lock reorder shipped in fix.diff built and ran clean across the full DF-3363/3364 PoC matrix (no deadlocks, no regressions), but since the race itself never reproduced at runtime, fix efficacy for the remaining windows cannot be behaviorally demonstrated; the select_bss/join windows need refcounting (recommendation only).", "fix_evidence": "findings/poc/DF-3363/fix_validation.log" } |