DF-2777 / verdict.json
{ "finding_id": "DF-2777", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Reproduced on the stock INVARIANTS guest with dormant stall hooks compiled into kern_intr.c at the exact window boundaries the finding cites (after info->i_state = ISTATE_NORMAL at kern_intr.c:326 before the i_thread allocation/creation, and at the emergency_intr_thread NULL check at :308). SC1: a second concurrent register_int(254, INTR_MPSAFE|INTR_CLOCK, cpu2) observed ISTATE_NORMAL with a NULL i_thread and faulted writing TDF_CLKTHREAD at kern_intr.c:349 (Stopped at register_int+0x1b9 lock orl 0x20000000,0xd8 rdx; VA 0xd8 = offsetof td_flags). SC2: with the first registrant stalled mid-window and the second rec linked, sched_ithd_soft(255) faulted reading info->i_thread->td_gd at kern_intr.c:589 (Stopped at sched_ithd_intern+0x1f movq 0xb8 rdx; VA 0xb8). SC3: two concurrent first-registrations on cpu2 (rig reopened the boot-time emergency window via dfh_reset_emergency, since a booted system has already created each cpu emergency thread) both ran systimer_init_periodic_nq on emergency_intr_timer[2]; the second re-init of an armed timer crashed inside systimer_add (Stopped at systimer_add+0x15c, VA 0, thread CRIT; the kern_systimer.c:148 SYSTF_ONQUEUE assert is bypassed because re-init wipes flags). Threat model: all triggers are kernel-internal (driver probe/attach races, boot-time first-registration concurrency, interrupt arrival during first registration); unprivileged users cannot drive register_int, so the ceiling is a kernel panic (DoS) at attach/boot time; severity Low as filed. Fix validated on a rebuilt kernel with the same orchestration: SC1 and SC3 print non-NULL results and survive, SC2 degrades to a rate-limited stray-interrupt report, no panics.", "exploit_chain": "", "evidence": [ "panic_sc1.txt: Stopped at register_int+0x1b9 lock orl 0x20000000,0xd8 rdx -- TDF_CLKTHREAD store through NULL i_thread", "panic_sc2.txt: DF2777-SC2 calling sched_ithd_soft(255) ... Stopped at sched_ithd_intern+0x1f movq 0xb8 rdx -- td_gd read through NULL i_thread", "panic_sc3.txt: Stopped at systimer_add+0x15c movq rcx rsi (VA 0, CRIT) -- double concurrent systimer_init_periodic_nq", "df2777_fixed.dmesg: SC1/SC2/SC3 survived on the fixed kernel (stray report for SC2)", "run.log: full stock and fixed A/B transcript", "src/kern_intr.hooks_stock.diffside.c and src/kern_intr.pristine.c: exact hook placement vs stock" ], "kernel_refs": [ "sys/kern/kern_intr.c:308", "sys/kern/kern_intr.c:326", "sys/kern/kern_intr.c:327", "sys/kern/kern_intr.c:349", "sys/kern/kern_intr.c:589", "sys/kern/kern_systimer.c:148" ], "poc_changes": "Runtime kldload GPFs on this guest, so the interposition is compiled into kern_intr.c as two dormant hook calls (df2777_hook) placed exactly at the window boundaries, plus a rig-only dfh_reset_emergency helper for SC3 (boot-time window reopen, labeled as rig). The stall hooks are no-ops unless armed via sysctl dfh.", "attempts": 3, "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": 2400, "guest_dirty": 0, "build_cmd": "cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC (stock+hooks build build.log.gz; fixed+hooks build build.fixed.log.gz)", "run_cmd": "sysctl dfh.scen2777=1 dfh.run2777=1 ; sysctl dfh.scen2777=2 dfh.run2777=1 ; sysctl dfh.scen2777=3 dfh.run2777=1", "code_hash": "9d114ea5b0e9cd57db016d471fbab552ae4dad52d85367e6aab84237b8c0fefc", "notes": "SC3 needs the rig reopen because a booted system already created every cpu emergency thread (first boot registration wins); the underlying code defect is the ungated check-then-create. The kern_systimer.c:148 INVARIANTS assert does not prevent the crash because re-initialization wipes the flags first. Guest reset with-src after evidence collection.", "recommended_fix": "Gate ithread creation with atomic_cmpset into a new ISTATE_CREATING state and publish i_thread before i_state; NULL-guard the INTR_CLOCK flag store and treat NULL i_thread in sched_ithd_intern as a stray; gate the emergency-thread block with a once-only cmpset (see fix.diff)." } |