DF-0726 / run.log
=== DF-0726 baseline race on UNPATCHED kernel (#0, 6.5-DEVELOPMENT Thu Jul 2 06:02:54 UTC 2026) ===
Attempt 1 (reader.c, 1 thread, 25s writer if_tap kldload/kldunload):
reader: 38,453,248 iters, total=7 (consistent, no EFAULT/ENOMEM)
writer: 4598 kldload/kldunload cycles of if_tap.ko
result: guest UP, no panic, no new boot.log content
reader stderr tail:
[reader] 38387712 iters, total=7
[reader] 38404096 iters, total=7
[reader] 38420480 iters, total=7
[reader] 38436864 iters, total=7
[reader] 38453248 iters, total=7
Attempt 2 (reader2.c, 8 threads, madvise-widened, 55s writer 3-module cycle):
reader: 114,777,878 total iters across 8 threads
writer: 9411 iterations x 3 modules (if_tap/if_vlan/if_gre) = ~28k load/unload cycles
result: guest UP, no panic, no new boot.log content
reader stderr: [reader2] 114777878 total iters across 8 threads
Attempt 3 (reader2.c, 16 threads, madvise-widened, 170s writer 5-module cycle):
reader: 151,259,361 total iters across 16 threads
writer: ~170s cycling if_tap/if_vlan/if_gre/if_bridge/if_wg (writer killed by tool timeout but reader confirmed alive)
result: guest UP, no panic, no new boot.log content
reader stderr: [reader2] 151259361 total iters across 16 threads
TOTAL across 3 attempts: ~265M+ reader iterations, ~50k+ writer load/unload cycles, ZERO panics.
=== Code-level trace (the race IS real, just too narrow to hit) ===
Reader (unprivileged): SIOCIFGCLONERS -> if_clone_list() at sys/net/if_clone.c:201-230
- line 207: reads if_cloners_count (NO LOCK)
- line 216: reads if_cloners_count again (NO LOCK)
- line 219-221: LIST_FIRST + LIST_FOREACH traversal (NO LOCK)
- line 223: reads ifc->ifc_name (NO LOCK)
Reachability: sys/net/if.c:2017-2018, NO caps_priv_check (confirmed unprivileged)
Writer (root, kldload/kldunload):
if_clone_attach() at if_clone.c:141-183:
- line 147: LIST_FOREACH dup check (NO LOCK)
- line 166: LIST_INSERT_HEAD (NO LOCK, before ifnet_lock at 169)
- line 167: if_cloners_count++ (NO LOCK)
if_clone_detach() at if_clone.c:189-195:
- line 192: LIST_REMOVE (NO LOCK)
- line 193: kfree(ifc->ifc_units) (NO LOCK)
- line 194: if_cloners_count-- (NO LOCK)
if_clone_lookup() at if_clone.c:296-306:
- line 300: LIST_FOREACH (NO LOCK), called from if_clone_create:67 + if_clone_destroy:116
Page unmap (the UAF sink): linker_file_unload() -> file->ops->unload() ->
link_elf_obj_unload_file() at sys/kern/link_elf_obj.c:904 vm_map_remove(kernel_map, ...)
The race window is between if_clone_detach() LIST_REMOVE (if_clone.c:192) and
vm_map_remove() (link_elf_obj.c:904). During this window (which spans the rest
of the module's MOD_UNLOAD handler + SYSUNINITs + sysctl unregister), a reader
holding a pointer to the detaching ifc can dereference freed/unmapped memory.
The reader's vulnerable window per iteration is ~50ns (between LIST_NEXT returning
&tap_cloner and the subsequent ifc->ifc_name read), making the race extremely
narrow despite the writer's wider unload window.
=== Verdict ===
status: not_reproduced (race confirmed in code, too narrow to trigger in practice)
impact: none (no panic observed; potential impact is panic/DoS if race were hit)
The writer requires root (kldload/kldunload), so this is NOT independently unprivileged.