DF-1927 / fix.diff
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, ¤t->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, ¤t->kt_flags)) { + complete(¤t->parked); + lwkt_deschedule_self(curthread); + } } |