DF-2917 / verdict.json
{ "finding_id": "DF-2917", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "REPRODUCED end-to-end. _sleepq_wait_begin() (sys/kern/subr_sleepqueue.c:317-340) computes 'domain = PDOMAIN_FBSD0 + td->td_sqqueue * PDOMAIN_FBSDINC' at :327 into a local that is then never used; tsleep() is called at :331/:336 with tflags alone. PINTERLOCKED (0x400, sys/sys/param.h:326) carries no domain bits, while sleepq_add() interlocked the thread with tsleep_interlock(wchan, PDOMAIN_FBSD0+...) recording td_wdomain = 0x01000000 (subr_sleepqueue.c:275-276, kern_synch.c:446). tsleep()'s interlock validation (kern_synch.c:660-663, td->td_wdomain != (flags & PDOMAIN_MASK)) therefore always mismatches and takes the 'goto resume' fast path: every sleepq_wait(), sleepq_wait_sig(), sleepq_timedwait() and sleepq_timedwait_sig() (subr_sleepqueue.c:387-458) returns immediately (ret 0) WITHOUT ever descheduling. Demonstrated on kernel #2 (DF-0139 mask + DF-2915 fix): a kthread executed sleepq_lock/sleepq_add/sleepq_wait with NO waker at all and sleepq_wait() returned ret=0 after 0 sbticks ('round1 (nowake): sleepq_wait() returned ret=0 after 0 sbticks with NO wakeup issued'). For any FreeBSD-compat/Linux-KPI consumer this turns every blocking wait into a busy-spin at kernel priority (per-CPU livelock) and every timeout into an instant success return - the blocking primitive the API promises does not exist. Fix validated on kernel #3: with tflags|domain (fix.diff, the established umtx pattern kern_umtx.c:227) the same no-wake sleeper genuinely blocked for ~200 ticks until the harness issued sleepq_broadcast() (woke in 1 tick on the follow-up round). Reachability: zero in-tree callers; kldload-gated (root).", "exploit_chain": "", "evidence": [ "run.log - kernel #2 (mask+DF-2915 fix, DF-2917 live): 'SQDEMO e2e: round1 (nowake): sleepq_wait() returned ret=0 after 0 sbticks with NO wakeup issued' - instantaneous return, zero blocking", "run.2.log - kernel #3 (both fixes): 'round1: sleeper STILL BLOCKED after ~200 ticks with no wakeup - sleepq_wait() really sleeps (DF-2917 fixed); issuing broadcast' and 'round2 (broadcast): sleepq_wait() returned after 1 sbticks'", "fix.diff - tflags | domain in both tsleep() calls (variable already existed, dead, at :327)", "VERDICT.md/README.md - full source trace kern_synch.c:660-663 -> goto resume" ], "kernel_refs": [ "sys/kern/subr_sleepqueue.c:275", "sys/kern/subr_sleepqueue.c:276", "sys/kern/subr_sleepqueue.c:317", "sys/kern/subr_sleepqueue.c:327", "sys/kern/subr_sleepqueue.c:331", "sys/kern/subr_sleepqueue.c:336", "sys/kern/subr_sleepqueue.c:387", "sys/kern/kern_synch.c:660", "sys/sys/param.h:326", "sys/kern/kern_umtx.c:227" ], "poc_changes": "Authored from scratch. The demonstrator is sqe2e.c (shared with the DF-2915 pack): round 1 runs lock/add/wait with no waker and times how long sleepq_wait() blocks; round 2 verifies broadcast wakeups. Required the DF-0139 test mask + DF-2915 fix kernels (see ../DF-2915) so the code path is reachable at all.", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Thu Sep 3 09:16:39 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 600, "guest_dirty": 0, "build_cmd": "cd /tmp/sqdemo/sqe2e && make (module; see ../DF-2915/build.sh)", "run_cmd": "kldload /root/sqe2e.ko ; sleep 1 ; dmesg | tail", "code_hash": "9fa6c75f5ee70aa5f6a44b564f087fe8b4bbab8fcb52b41c45aa0a0ef5f47f89", "notes": "Trigger source sqe2e.c shared with DF-2915 (same file, same hash). Baseline kernel #2 (Sep 3 09:16:39, labelled '#1'): instant return. Fixed kernel #3 (Sep 3 09:26:13, labelled '#2'): real 200-tick block + 1-tick broadcast wake. The dead 'domain' local at :327 is the smoking gun for the intended tflags|domain.", "recommended_fix": "Pass the already-computed domain into tsleep(): tsleep(td->td_wchan, tflags | domain, td->td_wmesg, ...) in both branches of _sleepq_wait_begin() (subr_sleepqueue.c:331/:336).", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #2: Thu Sep 3 09:26:13 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied in the guest /usr/src on top of mask+DF-2915-fix, kernel rebuilt (kbuild3_fix2917.log, rc=0) and installed. The identical no-wake demonstrator that returned in 0 ticks on the baseline now blocks for ~200 ticks until a broadcast is issued (and a subsequent round wakes in 1 tick). Blocking semantics fully restored; fix_status: fixed.", "fix_evidence": "run.2.log on kernel #3 ('STILL BLOCKED after ~200 ticks ... really sleeps'; 'round2 (broadcast): returned after 1 sbticks'); kbuild3_fix2917.log (full build log)." } |