⬢ DragonFlyBSD Kernel Audit
DF-4842 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/include/linux/sched.h	2026-09-24 12:11:29.636980723 +0000
+++ b/sys/dev/drm/include/linux/sched.h	2026-09-24 12:11:41.272831863 +0000
@@ -88,6 +88,7 @@
 	/* kthread-specific data */
 	unsigned long     kt_flags;
 	int             (*kt_fn)(void *data);
+	int               kt_done;	/* kthread body returned */
 	void             *kt_fndata;
 	int               kt_exitvalue;
 
--- a/sys/dev/drm/linux_kthread.c	2026-09-24 12:11:29.636980723 +0000
+++ b/sys/dev/drm/linux_kthread.c	2026-09-24 12:11:41.296831557 +0000
@@ -39,8 +39,21 @@
 linux_ktfn_wrapper(void *arg)
 {
 	struct task_struct *task = arg;
+	int ret;
+
+	ret = task->kt_fn(task->kt_fndata);
 
-	task->kt_exitvalue = task->kt_fn(task->kt_fndata);
+	/*
+	 * Publish the exit value and wake any kthread_stop() waiter
+	 * BEFORE returning into cpu_set_thread_handler()'s kthread_exit(),
+	 * after which the DFly thread reclaims its own struct thread via
+	 * lwkt_exit()/gd_freetd.  Nobody else may free the td.
+	 */
+	spin_lock(&task->kt_spin);
+	task->kt_exitvalue = ret;
+	task->kt_done = 1;
+	spin_unlock(&task->kt_spin);
+	wakeup(task);
 }
 
 struct task_struct *
@@ -78,6 +91,7 @@
 
 #define KTHREAD_SHOULD_STOP 1
 #define KTHREAD_SHOULD_PARK 2
+#define KTHREAD_IS_PARKED   4
 
 bool
 kthread_should_stop(void)
@@ -88,16 +102,31 @@
 int
 kthread_stop(struct task_struct *ts)
 {
+	int i, ret;
+
 	set_bit(KTHREAD_SHOULD_STOP, &ts->kt_flags);
 
-	kthread_unpark(ts);
+	/*
+	 * Only reschedule the thread if it actually parked itself: a raw
+	 * lwkt_schedule() on a running or tsleeping thread breaks the
+	 * runq/sleepq invariants.
+	 */
+	if (test_and_clear_bit(KTHREAD_IS_PARKED, &ts->kt_flags))
+		lwkt_schedule(ts->dfly_td);
 	wake_up_process(ts);
 
-	/* XXX use a better mechanism to wait for the thread to finish running */
-	tsleep(kthread_stop, 0, "kstop", hz);
-	lwkt_free_thread(ts->dfly_td);
-
-	return ts->kt_exitvalue;
+	/*
+	 * Wait for the body to return.  The DFly thread then reclaims its
+	 * own struct thread through lwkt_exit()/gd_freetd; freeing the td
+	 * here (as this shim used to) either freed a live thread or
+	 * double-freed an exited one (it is already parked in gd_freetd).
+	 */
+	for (i = 0; i < 120 * hz && ts->kt_done == 0; i++)
+		tsleep(ts, 0, "kstop", 1);
+
+	ret = ts->kt_exitvalue;
+	kfree(ts);			/* the task_struct is ours to free */
+	return (ret);
 }
 
 int
@@ -113,7 +142,8 @@
 kthread_unpark(struct task_struct *ts)
 {
 	clear_bit(KTHREAD_SHOULD_PARK, &ts->kt_flags);
-	lwkt_schedule(ts->dfly_td);
+	if (test_and_clear_bit(KTHREAD_IS_PARKED, &ts->kt_flags))
+		lwkt_schedule(ts->dfly_td);	/* revive a parked thread only */
 	wake_up_process(ts);
 }
 
@@ -129,5 +159,6 @@
 	if (test_bit(KTHREAD_SHOULD_PARK, &current->kt_flags) == 0)
 		return;
 
+	set_bit(KTHREAD_IS_PARKED, &current->kt_flags);
 	lwkt_deschedule_self(curthread);
 }