DragonFlyBSD Kernel Audit
DF-1927 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/include/linux/sched.h b/sys/dev/drm/include/linux/sched.h
--- a/sys/dev/drm/include/linux/sched.h
+++ b/sys/dev/drm/include/linux/sched.h
@@ -91,6 +91,12 @@
 	void             *kt_fndata;
 	int               kt_exitvalue;
 
+	/* DF-1927: completion signaled by kthread_parkme() when the target
+	 * thread has parked; kthread_park() blocks on it so the barrier at
+	 * drm_sched_entity_fini (sched_entity.c:277) actually waits, matching
+	 * upstream Linux wait_for_completion(&k->parked). */
+	struct completion parked;
+
 	/* executable name without path */
 	char              comm[TASK_COMM_LEN];
 

diff --git a/sys/dev/drm/linux_kthread.c b/sys/dev/drm/linux_kthread.c
--- a/sys/dev/drm/linux_kthread.c
+++ b/sys/dev/drm/linux_kthread.c
@@ -70,6 +70,10 @@
 	task->kt_fndata = data;
 	spin_init(&task->kt_spin, "tspin1");
 
+	/* DF-1927: initialize the park completion so kthread_park() can block
+	 * until the target actually parks. */
+	init_completion(&task->parked);
+
 	/* Start the thread here */
 	lwkt_schedule(td);
 
@@ -103,8 +107,18 @@
 int
 kthread_park(struct task_struct *ts)
 {
+	/* DF-1927: make kthread_park() synchronous, matching upstream Linux
+	 * which blocks on wait_for_completion(&k->parked).  Without this,
+	 * drm_sched_entity_fini's barrier at sched_entity.c:277 is a no-op,
+	 * allowing the scheduler kthread to run drm_sched_entity_pop_job()
+	 * concurrently with fini -> spsc_queue double-pop / dma_fence_put
+	 * kref underflow -> heap UAF. */
+	if (ts == current)
+		return ts->kt_exitvalue;
+
 	set_bit(KTHREAD_SHOULD_PARK, &ts->kt_flags);
 	wake_up_process(ts);
+	wait_for_completion(&ts->parked);
 
 	return ts->kt_exitvalue;
 }
@@ -126,8 +140,14 @@
 void
 kthread_parkme(void)
 {
-	if (test_bit(KTHREAD_SHOULD_PARK, &current->kt_flags) == 0)
-		return;
-
-	lwkt_deschedule_self(curthread);
+	/* DF-1927: loop signaling completion each iteration so kthread_park()
+	 * can reliably observe that we've parked.  Matches upstream Linux
+	 * __kthread_parkme().  Without the complete() the parking thread
+	 * (kthread_park) would block forever; without the loop a spurious
+	 * wakeup that doesn't clear KTHREAD_SHOULD_PARK would lose the
+	 * completion handshake. */
+	while (test_bit(KTHREAD_SHOULD_PARK, &current->kt_flags)) {
+		complete(&current->parked);
+		lwkt_deschedule_self(curthread);
+	}
 }