From 11e451e74050d9e9030581ce40337838acfcea5b Mon Sep 17 00:00:00 2001 From: Ben Skeggs <bskeggs@redhat.com> Date: Tue, 8 May 2018 20:39:47 +1000 Subject: [PATCH] drm/nouveau: remove fence wait code from deferred client work handler Fences attached to deferred client work items now originate from channels belonging to the client, meaning we can be certain they've been signalled before we destroy a client. This closes a race that could happen if the dma_fence_wait_timeout() call didn't succeed. When the fence was later signalled, a use-after-free was possible. Signed-off-by: Ben Skeggs <bskeggs@redhat.com> --- drivers/gpu/drm/nouveau/nouveau_drm.c | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 6caece4f2f5f6..64b8fd0c4d68f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -113,24 +113,22 @@ nouveau_name(struct drm_device *dev) } static inline bool -nouveau_cli_work_ready(struct dma_fence *fence, bool wait) +nouveau_cli_work_ready(struct dma_fence *fence) { - if (!dma_fence_is_signaled(fence)) { - if (!wait) - return false; - WARN_ON(dma_fence_wait_timeout(fence, false, 2 * HZ) <= 0); - } + if (!dma_fence_is_signaled(fence)) + return false; dma_fence_put(fence); return true; } static void -nouveau_cli_work_flush(struct nouveau_cli *cli, bool wait) +nouveau_cli_work(struct work_struct *w) { + struct nouveau_cli *cli = container_of(w, typeof(*cli), work); struct nouveau_cli_work *work, *wtmp; mutex_lock(&cli->lock); list_for_each_entry_safe(work, wtmp, &cli->worker, head) { - if (!work->fence || nouveau_cli_work_ready(work->fence, wait)) { + if (!work->fence || nouveau_cli_work_ready(work->fence)) { list_del(&work->head); work->func(work); } @@ -158,17 +156,17 @@ nouveau_cli_work_queue(struct nouveau_cli *cli, struct dma_fence *fence, mutex_unlock(&cli->lock); } -static void -nouveau_cli_work(struct work_struct *w) -{ - struct nouveau_cli *cli = container_of(w, typeof(*cli), work); - nouveau_cli_work_flush(cli, false); -} - static void nouveau_cli_fini(struct nouveau_cli *cli) { - nouveau_cli_work_flush(cli, true); + /* All our channels are dead now, which means all the fences they + * own are signalled, and all callback functions have been called. + * + * So, after flushing the workqueue, there should be nothing left. + */ + flush_work(&cli->work); + WARN_ON(!list_empty(&cli->worker)); + usif_client_fini(cli); nouveau_vmm_fini(&cli->vmm); nvif_mmu_fini(&cli->mmu); -- GitLab