⬢ DragonFlyBSD Kernel Audit
DF-5017 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/linux_fence.c
+++ b/sys/dev/drm/linux_fence.c
@@ -174,8 +174,7 @@
 {
 	struct default_wait_cb *cb;
 	long ret = timeout;
-	unsigned long end;
-	int i, err;
+	int i;
 
 	if (timeout == 0) {
 		for (i = 0; i < count; i++) {
@@ -203,14 +202,42 @@
 		}
 	}
 
-	end = jiffies + timeout;
-	for (ret = timeout; ret > 0; ret = MAX(0, end - jiffies)) {
-		if (dma_fence_test_signaled_any(fences, count, idx))
-			break;
-		err = tsleep(current, intr ? PCATCH : 0, "dfwat", ret);
-		if (err == EINTR || err == ERESTART) {
-			ret = -ERESTARTSYS;
-			break;
+	/*
+	 * Use the schedule_timeout() shim: its kt_spin + task-state latch
+	 * closes the check-to-sleep wakeup race, it clamps the tick count
+	 * (no long->int truncation of the sleep timeout), and it returns
+	 * non-negative remaining time (no unsigned end-jiffies wrap).
+	 */
+	if (timeout == MAX_SCHEDULE_TIMEOUT) {
+		for (;;) {
+			if (dma_fence_test_signaled_any(fences, count, idx)) {
+				ret = 1;
+				break;
+			}
+			__set_current_state(intr ? TASK_INTERRUPTIBLE :
+			    TASK_UNINTERRUPTIBLE);
+			schedule_timeout(MAX_SCHEDULE_TIMEOUT);
+			__set_current_state(TASK_RUNNING);
+			if (intr && signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+	} else {
+		unsigned long end = jiffies + timeout;
+
+		for (ret = timeout; ret > 0;
+		    ret = MAX(0L, (long)(end - jiffies))) {
+			if (dma_fence_test_signaled_any(fences, count, idx))
+				break;
+			__set_current_state(intr ? TASK_INTERRUPTIBLE :
+			    TASK_UNINTERRUPTIBLE);
+			schedule_timeout(ret);
+			__set_current_state(TASK_RUNNING);
+			if (intr && signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
 		}
 	}