drm/i915/vma: Fix UAF on destroy against retire race
authorjsg <jsg@openbsd.org>
Mon, 29 Apr 2024 06:27:37 +0000 (06:27 +0000)
committerjsg <jsg@openbsd.org>
Mon, 29 Apr 2024 06:27:37 +0000 (06:27 +0000)
From Janusz Krzysztofik
5e3eb862df9f972ab677fb19e0d4b9b1be8db7b5 in linux-6.6.y/6.6.29
0e45882ca829b26b915162e8e86dbb1095768e9e in mainline linux

sys/dev/pci/drm/i915/i915_vma.c

index 21237cd..3727ff3 100644 (file)
@@ -33,6 +33,7 @@
 #include "gt/intel_engine.h"
 #include "gt/intel_engine_heartbeat.h"
 #include "gt/intel_gt.h"
+#include "gt/intel_gt_pm.h"
 #include "gt/intel_gt_requests.h"
 #include "gt/intel_tlb.h"
 
@@ -110,12 +111,34 @@ static inline struct i915_vma *active_to_vma(struct i915_active *ref)
 
 static int __i915_vma_active(struct i915_active *ref)
 {
-       return i915_vma_tryget(active_to_vma(ref)) ? 0 : -ENOENT;
+       struct i915_vma *vma = active_to_vma(ref);
+
+       if (!i915_vma_tryget(vma))
+               return -ENOENT;
+
+       /*
+        * Exclude global GTT VMA from holding a GT wakeref
+        * while active, otherwise GPU never goes idle.
+        */
+       if (!i915_vma_is_ggtt(vma))
+               intel_gt_pm_get(vma->vm->gt);
+
+       return 0;
 }
 
 static void __i915_vma_retire(struct i915_active *ref)
 {
-       i915_vma_put(active_to_vma(ref));
+       struct i915_vma *vma = active_to_vma(ref);
+
+       if (!i915_vma_is_ggtt(vma)) {
+               /*
+                * Since we can be called from atomic contexts,
+                * use an async variant of intel_gt_pm_put().
+                */
+               intel_gt_pm_put_async(vma->vm->gt);
+       }
+
+       i915_vma_put(vma);
 }
 
 static struct i915_vma *
@@ -1413,7 +1436,7 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww,
        struct i915_vma_work *work = NULL;
        struct dma_fence *moving = NULL;
        struct i915_vma_resource *vma_res = NULL;
-       intel_wakeref_t wakeref = 0;
+       intel_wakeref_t wakeref;
        unsigned int bound;
        int err;
 
@@ -1433,8 +1456,14 @@ int i915_vma_pin_ww(struct i915_vma *vma, struct i915_gem_ww_ctx *ww,
        if (err)
                return err;
 
-       if (flags & PIN_GLOBAL)
-               wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
+       /*
+        * In case of a global GTT, we must hold a runtime-pm wakeref
+        * while global PTEs are updated.  In other cases, we hold
+        * the rpm reference while the VMA is active.  Since runtime
+        * resume may require allocations, which are forbidden inside
+        * vm->mutex, get the first rpm wakeref outside of the mutex.
+        */
+       wakeref = intel_runtime_pm_get(&vma->vm->i915->runtime_pm);
 
        if (flags & vma->vm->bind_async_flags) {
                /* lock VM */
@@ -1570,8 +1599,7 @@ err_fence:
        if (work)
                dma_fence_work_commit_imm(&work->base);
 err_rpm:
-       if (wakeref)
-               intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref);
+       intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref);
 
        if (moving)
                dma_fence_put(moving);