⬢ DragonFlyBSD Kernel Audit
DF-4740 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/scheduler/sched_main.c	2026-09-23 06:03:21.388609645 +0000
+++ b/sys/dev/drm/scheduler/sched_main.c	2026-09-23 06:03:21.400609489 +0000
@@ -365,6 +365,22 @@
 		if (found_guilty && s_job->s_fence->scheduled.context == guilty_context)
 			dma_fence_set_error(&s_fence->finished, -ECANCELED);
 
+		/*
+		 * The finished fence is already signaled: a finish_work is
+		 * queued (or running) and owns removing this job from the
+		 * ring mirror list and freeing it.  Re-running the job here
+		 * would race finish_work across the unlocked run_job()
+		 * window below and arm callbacks inside freed memory.
+		 */
+		if (dma_fence_is_signaled(&s_fence->finished))
+			continue;
+
+		/*
+		 * Pin the fence chain across the unlocked run_job() window:
+		 * a completion racing in from another context must not be
+		 * able to drop the last reference while we still use it.
+		 */
+		dma_fence_get(&s_fence->finished);
 		lockmgr(&sched->job_list_lock, LK_RELEASE);
 		fence = sched->ops->run_job(s_job);
 		atomic_inc(&sched->hw_rq_count);
@@ -379,10 +395,12 @@
 				DRM_ERROR("fence add callback failed (%d)\n",
 					  r);
 			dma_fence_put(fence);
+			dma_fence_put(&s_fence->finished);
 		} else {
 			if (s_fence->finished.error < 0)
 				drm_sched_expel_job_unlocked(s_job);
 			drm_sched_process_job(NULL, &s_fence->cb);
+			dma_fence_put(&s_fence->finished);
 		}
 		lockmgr(&sched->job_list_lock, LK_EXCLUSIVE);
 	}
@@ -653,5 +671,6 @@
 {
 	if (sched->thread)
 		kthread_stop(sched->thread);
+	cancel_delayed_work_sync(&sched->work_tdr);
 }
 EXPORT_SYMBOL(drm_sched_fini);