DF-2212 / verdict.json
{ "finding_id": "DF-2212", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "REPRODUCED with the real production code on the QEMU guest via a verbatim-interposition kernel-compiled rig (kernel config DFV2212 = X86_64_GENERIC + device dfverify; harness df_ttm2212.c #includes the REAL sha256-gated sys/dev/drm/ttm/ttm_module.c, byte-identical guest/audited trees, plus verbatim lkpi linux_wait.c and linux_sched.c; the two drm_sysfs externs carry exactly the shipped no-op semantics of drm_sysfs.c:60-75). Control: the verbatim ttm_init ran at boot via SYSINIT (module.h:58-59), register_calls=1 and device_released=0 - the init SUCCESS path (ttm_module.c:77) - so the exit condition is dead from the start. Trigger: hw.df_ttm2212.trigger=1 called the real ttm_exit() in the calling user thread, which is the same thread-context linker_file_sysuninit (kern_linker.c:535) gives the kldunload(2) thread of drm.ko; the thread blocked FOREVER: ps state D0 with wchan lwe (the literal tsleep wmesg of wait_event at wait.h:135) at t=3s/6s/33s/93s, SIGKILL posted and pending in p_siglist in all 16 kernel-side watchdog samples over 80s yet never acted upon (unkillable), TDF_TSLEEPQ=YES, device_released=0 in every sample, the sysctl command never returned, and the watchdog verdict line records that no compiled code path in sys/ can set device_released=1 (the only setter is #if 0 at ttm_module.c:48-52; the init error-path setter at :84 cannot run because dev_set_name and drm_class_device_register are no-ops returning 0 on DF - device.h:106-110, drm_sysfs.c:60-63). The sleep is uninterruptible by construction: wait_event passes flags=0, no PCATCH, TASK_UNINTERRUPTIBLE (wait.h:110-172). FIXED KERNEL (same tree + fix.diff, sha256-gated 4faabe2f...): ttm_exit() RETURNED after 0 ms with device_released still 0 (proving the wait was removed, not the condition set), 500 x (ttm_init;ttm_exit) cycles all returned, no stuck processes, no other behavior change. Impact ceiling: root-triggered (kldunload drm / unload cascade) unkillable D-state thread plus a half-torn module (LINKER_FILE_LINKED already cleared before the blocking sysuninit, kern_linker.c:531-535) - a permanent DoS wedge of the unload operation on any drm-bearing system. Honest refinement of the filed claim: DF shutdown does NOT run module SYSUNINITs (kern_shutdown.c has no linker/sysuninit walk), so the shutdown arm of the claim is source-dead; kldunload is the live path. No unprivileged reach, no memory corruption, no privilege boundary crossed - privesc directive closed as not applicable; Low severity confirmed.", "exploit_chain": "", "evidence": "run.log (decisive baseline output: ps D0/lwe snapshots at t=3/6/33/93s, status sysctls with exit_done=0 exit_ms=-1, kill -9 marker tick 5963, full 16-sample watchdog block and WATCHDOG VERDICT line, trigger.out shows no exit line); dmesg.txt (kernel-side TRIGGER backtrace print at wait entry: userland_sysctl/sys___sysctl/syscall2 - a user syscall thread, plus baseline watchdog era and fixed era side by side thanks to warm-reboot msgbuf survival); run_fixed.log (trigger completes 0.07 real, ttm_exit RETURNED after 0 ms, CYCLE 500 completed, none stuck); build.log and fix_build.log (full untrimmed -Werror kernel builds, rc=0); VERDICT.md (full narrative incl. the no-other-setter grep proof); fix.diff (one-hunk, git-apply-able, verified byte-exact against the tested fixed file)", "kernel_refs": [ "sys/dev/drm/ttm/ttm_module.c:37", "sys/dev/drm/ttm/ttm_module.c:38", "sys/dev/drm/ttm/ttm_module.c:40", "sys/dev/drm/ttm/ttm_module.c:48", "sys/dev/drm/ttm/ttm_module.c:50", "sys/dev/drm/ttm/ttm_module.c:56", "sys/dev/drm/ttm/ttm_module.c:77", "sys/dev/drm/ttm/ttm_module.c:84", "sys/dev/drm/ttm/ttm_module.c:98", "sys/dev/drm/include/linux/wait.h:110", "sys/dev/drm/include/linux/wait.h:121", "sys/dev/drm/include/linux/wait.h:130", "sys/dev/drm/include/linux/wait.h:135", "sys/dev/drm/include/linux/wait.h:171", "sys/dev/drm/include/linux/module.h:58", "sys/dev/drm/include/linux/module.h:60", "sys/dev/drm/include/linux/device.h:106", "sys/dev/drm/drm_sysfs.c:60", "sys/dev/drm/drm_sysfs.c:73", "sys/dev/drm/linux_sched.c:59", "sys/kern/kern_linker.c:188", "sys/kern/kern_linker.c:533", "sys/kern/kern_linker.c:535" ], "poc_changes": "No seeded pack existed; authored the whole rig per the directive. Runtime kldload of drm GPFs on this guest (established), so the rig compiles the verbatim code into a custom kernel: harness df_ttm2212.c includes the real tree file by path so the baseline vs fixed A/B exercises exactly the tree under test; lkpi linux_wait.c and linux_sched.c included verbatim to close the wait/sched symbols (the lkpi current macro needs linux_task_alloc); config(8) rejects digits in device names so the pseudo-device is dfverify, not dfv2212; scripts and logs live under /root/poc (tmpfs /tmp is wiped each boot); build.sh sha256-gates the tree file for both modes.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #2: Tue Oct 20 22:05:26 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/DFV2212 x86_64 (baseline rig kernel built #1 21:46:59, fixed #2 22:05:26; stock INVARIANTS base)", "runtime_sec": 4200, "guest_dirty": 0, "build_cmd": "sh build.sh baseline (then: cp ttm_module_fixed.c /usr/src/sys/dev/drm/ttm/ttm_module.c; sh build.sh fixed) - make -j6 nativekernel KERNCONF=DFV2212", "run_cmd": "sh run.sh (baseline kernel); sh run_fixed.sh (fixed kernel)", "code_hash": "84e12aaa7db293d28995d9bd64dd1a3941938e75a880f931d1cf790d45fe1a3f", "notes": "Unkillable-proof detail: SIGKILL was delivered to the blocked trigger process (kernel kill9 marker set, p_siglist SIGKILL bit observed set in every watchdog sample) and the process still existed at t=93s - the sleep has no PCATCH so signals are posted but never acted on. ps STAT letter on DF for the blocked thread is D0 with wchan lwe. Shutdown arm of the original claim killed by source: DF never walks built-in or module sysuninit_set at shutdown (only kern_linker.c walks it, from unload paths), so kldunload is the sole live trigger - root-only. llf_lock is released around the blocking sysuninit (kern_linker.c:533-535) so the wedge is the unkillable thread plus the half-torn module, not a global linker-lock deadlock. Not memcorrupt; no unprivileged path; TEAM DIRECTIVE privesc: closed, not applicable. Guest reset to clean-source snapshot after verification.", "recommended_fix": "Delete the dead wait_event (and its comment) from ttm_exit - the author comment itself doubts it; verified fix.diff removes exactly ttm_module.c:93-98", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #2: Tue Oct 20 22:05:26 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/DFV2212 x86_64 (fix_build.log rc=0 under -Werror)", "fix_verdict": "fixed: baseline kernel (pristine tree file) blocked the calling thread forever with SIGKILL ignored; identical kernel with only fix.diff applied (tree file sha256 4faabe2f... verified before build) returned from ttm_exit in 0 ms, completed 500 init/exit cycles, left no stuck processes, boot behavior unchanged, device_released still 0 proving the wait itself was the defect; bad behavior GONE post-fix", "fix_evidence": "fix.diff; fix_build.log (full untrimmed build, rc=0); run_fixed.log (0.07 real trigger, RETURNED after 0 ms, CYCLE 500 completed, none stuck); run.log (baseline hang for contrast); dmesg.txt (both eras)" } |