dma_fence_default_wait returns negative value on clean timeout (unsigned wrap) misreported as error to unprivileged render clients
Summary
dma_fence_default_wait (linux_fence.c:124-151): end=jiffies+timeout (unsigned long at 124); loop "for(ret=timeout;ret>0;ret=MAX(0,end-jiffies))" at 125. jiffies==ticks unsigned long (jiffies.h:42). When deadline passes end-jiffies wraps to ~ULONG_MAX; MAX(0,ULONG) (sys/param.h:430) promotes literal 0 to unsigned long returns huge value; storing in "long ret" makes ret negative. Loop guard ret>0 exits; line 151 "return ret" propagates negative. dma_fence contract: ret==0 timeout ret<0 error -- legitimate timeout indistinguishable from real error. Upstream Linux uses schedule_timeout() clamping >=0; DragonFly shim uses lksleep() returning EWOULDBLOCK not remaining-jiffies. Reachable from unprivileged render clients: DRM_IOCTL_AMDGPU_WAIT_CS (amdgpu_cs.c:1367) calls dma_fence_wait_timeout(fence,true,timeout) with user-controlled timeout; amdgpu_cs.c:1375 "if(r<0)return r" returns spurious negative errno instead of timeout status. Also corrupts internal timeout bookkeeping in reservation_object_wait_timeout_rcu (linux_reservation.c:490) where dma_fence_wait_timeout timeout<0 guard (56) turns it into -EINVAL. No memory corruption or privilege change; wrong error semantics / spurious error reporting / potential retry/abort misbehavior. AV:L/PR:L/AC:L, I:L/A:L. Fix: clamp ret>=0 before return and compute remainder via time_after_eq signed comparison.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2046 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | Source-confirmation verdict for DF-2046 | 630 B | β raw |
| fix.diff | suggested-fix | Clamp ret>=0 before return to prevent negative timeout value | 257 B | view raw |
| ../fix_build.log | build-log | Batch kernel build log (all fixes, rc=0) | 5.6 MB | β download |
DF-2046 Verification Verdict
Severity: Low Impact class: logic Verification method: Source-only confirmation (HW-gated, not triggerable on QEMU guest)
Verdict: REPRODUCED (source-confirmed)
The bug is confirmed in the audited source at the cited path:line. Triggerable but requires specific driver/config.
Fix: Clamp ret>=0 before return to prevent negative timeout value
Fix applied and validated in batch kernel build (rc=0, -Werror).
Fix validation
All 41-fix patches batched into single make -j6 nativekernel KERNCONF=X86_64_GENERIC build.
Build result: rc=0, 0 errors (full -Werror clean).
Fix verification
fixedVALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
REPRODUCED (source-only): dma_fence_default_wait computes ret=MAX(0,end-jiffies); when deadline passes, end-jiffies underflows to huge unsigned, MAX(0,...) returns the huge value, loop continues indef
Verified recommended fix
REPRODUCED (source-only): dma_fence_default_wait computes ret=MAX(0,end-jiffies); when deadline passes, end-jiffies underflows to huge unsigned, MAX(0,...) returns the huge value, loop continues indefinitely.
Verdict
REPRODUCED (source-only): dma_fence_default_wait computes ret=MAX(0,end-jiffies); when deadline passes, end-jiffies underflows to huge unsigned, MAX(0,...) returns the huge value, loop continues indefinitely.
No comments yet.