DF-5182 / verdict.json
{ "finding_id": "DF-5182", "status": "reproduced", "verdict": "REPRODUCED on the QEMU guest via a KLD (lwqdemo.ko) that compiles the pristine sys/dev/drm/linux_wait.c and drives the real wait_event_timeout() macro. In 9/9 windowed rounds where the waker performed the production protocol (store condition true, then wake_up()) inside the slot between the conditions state read and tsleep_interlock(), the wake was dropped, autoremove_wake_function() unlinked the wait entry (wake_up_process() always returns 1), a rescue wake_up() ~100us later could not reach the sleeper, the waiter burned its full 2s timeout and wait_event_timeout() returned 0 (condition never became true) although gen had been bumped before the wake. 8/8 control rounds (wake after the interlock) returned immediately with ret>0, proving the harness. A pure timing attack over 1,666,602 rounds produced 0 hits - the window is entered in production via condition width (e.g. radeon_fence_any_seq_signaled) or waiter preemption, both ordinary on loaded GPU systems. Impact: spurious timeout error paths and stalled waits in drm sync (radeon fence waits -> needless GPU-reset escalation, i915 reset waits -> -EAGAIN/-ERESTARTSYS to render clients, intel_dp gmbus failures).", "reproduced": true, "impact": "dos", "confidence": "certain", "exploit_chain": "unprivileged render client -> drm ioctl -> wait_event_timeout()/wait_event_interruptible_timeout() in kernel -> wake from another CPU lands between condition evaluation and tsleep_interlock (condition has width or waiter preempted) -> wakeup dropped + wait entry auto-removed -> callers full timeout burned and retval==0 (spurious timeout) or <=1s-stall for poll variants -> caller error paths (fence timeout escalation, reset-wait EAGAIN, gmbus AUX failure). No memory corruption; per-event DoS/functional-error class.", "evidence": "run.log: 9 MISREPORT (windowed) dt=199 ticks ... gen N != N-1 is TRUE lines + SUMMARY rounds_done=17 fast=8 control_ok=8 misreports=9 rescues_fired=9 + earlier statistical run rounds_done=1666602 ... misreports=0; VERDICT.md full narrative; lwqdemo.c harness (real macro + real linux_wait.c linked in).", "kernel_refs": [ "sys/dev/drm/include/linux/wait.h:110", "sys/dev/drm/include/linux/wait.h:127", "sys/dev/drm/include/linux/wait.h:130", "sys/dev/drm/linux_wait.c:37", "sys/dev/drm/linux_wait.c:43", "sys/dev/drm/linux_wait.c:49", "sys/dev/drm/include/linux/sched.h:229" ], "poc_changes": "Seed concept reworked entirely: harness is a KLD compiling the real linux_wait.c (guest kernel has no drm symbols); v1/v2 used an external timing-swept waker (1.67M rounds, 0 hits - documented as negative evidence that the window needs condition-width/preemption); v3 admits the waker inside the condition-expression window via a window_open/poke handshake, modeling production conditions with non-trivial width, with interleaved control rounds proving correct out-of-window behavior.", "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": 480, "guest_dirty": 0, "build_cmd": "cd /root/lwqdemo && make SYSDIR=/usr/src/sys (build.log, rc=0, -Werror)", "run_cmd": "kldload ./lwqdemo.ko && sleep 25 && dmesg | grep LWQ (run.log)", "code_hash": "1978c8c3c8887476ea99e9125e41040d68b2484aa4895ca87ab0ad09d9850632", "notes": "Guest was reset with-src after the run (clean for the next spawn). Module unloaded before reset. The 1.67M-round zero-hit statistical run is retained in run.log as the honest bound on externally-timed reachability. Relationship to known findings: DF-2181/DF-2182 cover wait_on_bit_timeout only; this finding is the wait_event macro family + autoremove unlink semantics, a different root cause and fix site.", "recommended_fix": "Order tsleep_interlock() before the condition check in __wait_event_common (wait.h) and make wake_up_process() return whether a wakeup was delivered so autoremove_wake_function() only unlinks entries that were actually woken.", "fix_status": "not_tested", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "fix.diff authored (wait.h reorder + sched.h wake_up_process return + linux_wait.c comment) but not kernel-rebuild-validated; Medium logic/race finding, changes are mechanical.", "fix_evidence": "fix.diff in this pack" } |