{
  "finding_id": "DF-5017",
  "status": "reproduced",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "dma_fence_wait_any_timeout (sys/dev/drm/linux_fence.c:206-215) misreports clean timeouts as negative errors (unsigned end-jiffies wrap: 399/400 waits returned ret<0 against a contract of 0), truncates its long timeout into tsleeps int timo so MAX_SCHEDULE_TIMEOUT waits degenerate into tick-rate poll loops ((int)LONG_MAX==-1 -> next-tick callout; measured 52 wakeups/s), and has an uninterlocked check-then-sleep window in which a dma_fence_signal() wakeup is permanently lost (proven at the primitive level: a wakeup fired nanoseconds after the waiters check left it sleeping the full 1s timeout while 200ms-control wakes were delivered). All three defects demonstrated on a fresh-reset guest against the shipped drm.ko implementation via a KLD that runs the baseline and a fixed schedule_timeout()-idiom copy side by side; the fixed copy scored 0/400 negatives and correct infinite-wait semantics. No memory corruption: impact is wrong synchronization semantics, spurious errors to unprivileged GPU clients (DRM_IOCTL_AMDGPU_WAIT_FENCES via amdgpu_cs.c:1560, amdgpu_gem_timeout clamps negative timeout_ns to MAX_SCHEDULE_TIMEOUT), and unbounded poll-churn/timeout-stalls (amdgpu_sa.c:322, intr=false).",
  "exploit_chain": "",
  "evidence": "run.2.log (clean run): [DF5017] TEST A ... baseline wait_any: ret<0:399 ret==0:1 ... fixed wait_any: ret<0:0 ret==0:400; TEST B baseline ret=9223372036854775507 after 300 ticks vs fixed ret=1; TEST E tsleep timo=(int)MAX_SCHEDULE_TIMEOUT=-1 -> 52 wakeups in 1s. run.log (first session): TEST C trial 4 (immediate): slept 100 ticks (WAKEUP LOST) with controls delivered at 19 ticks. fix.diff applies cleanly; fix_build.log shows the patched linux_fence.c compiles clean as a KLD with -Werror.",
  "kernel_refs": [
    "sys/dev/drm/linux_fence.c:206",
    "sys/dev/drm/linux_fence.c:207",
    "sys/dev/drm/linux_fence.c:208",
    "sys/dev/drm/linux_fence.c:210",
    "sys/dev/drm/include/linux/sched.h:78",
    "sys/kern/kern_timeout.c:968",
    "sys/kern/kern_synch.c:575",
    "sys/dev/drm/amd/amdgpu/amdgpu_sa.c:322",
    "sys/dev/drm/amd/amdgpu/amdgpu_cs.c:1560",
    "sys/dev/drm/amd/amdgpu/amdgpu_gem.c:414"
  ],
  "poc_changes": "PoC authored fresh for this finding (no seed). Iterations: (1) dropped <sys/lockmgr.h> (no such header), (2) fence arrays passed as struct dma_fence **, (3) first run panicked on a NULL kthread arg in my own signaler thread (fixed), (4) TEST C harness kthread lifecycle wedged the guest at the end of its run (harness artifact; TEST C retired from the automated flow, its trial-4 result kept in run.log), (5) final clean run: module loads, runs ~30s, unloads. File transport: direct ssh stream (guest b64decode/openssl were unreliable for large payloads).",
  "attempts": 4,
  "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": 210,
  "guest_dirty": 0,
  "build_cmd": "kldload drm && cd /tmp/df5017 && make",
  "run_cmd": "kldload /tmp/df5017/df5017.ko && dmesg | grep DF5017",
  "code_hash": "162c6d442885080b82ff84ce3e45e72c0d6f721278497ba35b6836504179b883",
  "notes": "Related to DF-2046 (identical wrap pattern in dma_fence_default_wait, lines 124-151, NOT re-reported); this finding is the separate wait_any copy of the pattern plus two defects DF-2046 does not cover (int truncation of timo, uninterlocked lost-wakeup window). The standard audit guest has no GPU, so the amdgpu ioctl paths were not directly exercisable; the primitive was verified against the live in-kernel implementation, which is driver-agnostic. Behavior is deterministic for defects 1-2 (399/400 and 100% across sessions); defect 3 is a genuine race (1/10 hits in the primitive demo).",
  "recommended_fix": "Replace the raw tsleep loop with the set_current_state()/schedule_timeout() idiom from the DF shim (latches wakeups via kt_spin+TASK_RUNNING, clamps timo, returns non-negative remainder) and handle MAX_SCHEDULE_TIMEOUT as a true indefinite wait.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff rewrites the wait loop with schedule_timeout(); validated (a) behaviorally by compiling the identical algorithm into the PoC module: fixed copy 0/400 negative returns vs baseline 399/400 and correct MAX_SCHEDULE_TIMEOUT return (ret=1 at signal, no poll), and (b) the patched linux_fence.c translation unit compiles clean as a kernel module with full -Werror flags (fix_build.log). Full fixed-kernel rebuild not performed (logic-class finding, not memory corruption).",
  "fix_evidence": "run.2.log TEST A fixed wait_any: ret<0:0 ret==0:400 and TEST B fixed ... ret=1 elapsed=300 ticks (correct); fix.diff (git apply --check clean); fix_build.log FIXED_TU_COMPILES_CLEAN."
}