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

Reply via email to