β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2046

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2046 Β· 3 files
FileTypeDescriptionSize
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
VERDICT.md verdict Source-confirmation verdict for DF-2046
↓ download raw

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

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: 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.
↓ fix.diffcombined build rc=0

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.