drm/sched: run queues freed before the TDR that drm_sched_fini() waits for

From: Donggeun Yoo

Date: Thu Sep 10 2026 - 01:47:12 EST


Hi Philipp,

drm_sched_fini() frees the run queues above the two steps that wait for
users of them:

for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++)
kfree(sched->sched_rq[i]);

/* Wakeup everyone stuck in drm_sched_entity_flush for this scheduler */
wake_up_all(&sched->job_scheduled);

/* Confirm no work left behind accessing device structures */
cancel_delayed_work_sync(&sched->work_tdr);

4827d6d83f07 ("drm/sched: Remove racy hack from drm_sched_fini()") did not
change that ordering - the kfree() was above the wakeup before it as well,
and has been since 56e449603f0a ("drm/sched: Convert the GPU scheduler to
variable number of run-queues") made the run queues separately allocated.
But with the loop body gone there no longer seems to be anything holding
the free up there.

A KUnit case that keeps the TDR inside timedout_job() while drm_sched_fini()
runs, with the callback calling drm_sched_increase_karma() as amdgpu does:

BUG: KASAN: slab-use-after-free in _raw_spin_lock+0x2b/0x40
Workqueue: events drm_sched_job_timedout
drm_sched_increase_karma+0x138/0x3e0
fini_uaf_timedout_job+0x4c/0x140
drm_sched_job_timedout+0x1b4/0x620
allocated by drm_sched_init+0x49c, freed by drm_sched_fini+0xec

Moving the loop down beside kfree(sched->sched_rq) silences it, and nothing
between the two positions reads the run queues. Is that the right fix, or is
the intended rule that the TDR can never still be running at that point?

Resent: the original did not reach dri-devel - I was not subscribed at the
time. Apologies to those seeing it twice.

Thanks,
Donggeun