4.17-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Ben Skeggs <[email protected]>

[ Upstream commit 11e451e74050d9e9030581ce40337838acfcea5b ]

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 <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
 drivers/gpu/drm/nouveau/nouveau_drm.c |   30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -116,24 +116,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);
                }
@@ -162,16 +160,16 @@ nouveau_cli_work_queue(struct nouveau_cl
 }
 
 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);


Reply via email to