gen6_ppgtt_create() stores the result of gen6_alloc_top_pd() directly
into ppgtt->base.pd, and on failure the ERR_PTR stays in the field.
i915_vm_put() runs vm->cleanup = gen6_ppgtt_cleanup(), where
gen6_ppgtt_free_pd() dereferences pd via gen6_for_all_pdes() - with an
ERR_PTR that is an invalid dereference and a crash (in the async
release worker).

Assign to a local first and store into the field only after it is
validated, so the field never holds an error pointer; on the failure
path it stays NULL, and gen6_ppgtt_free_pd() now bails out on a NULL
pd.

Found by static analysis.

Cc: [email protected]
Signed-off-by: Chengyu Liu <[email protected]>
---
 drivers/gpu/drm/i915/gt/gen6_ppgtt.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c 
b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
index 438cd4724ac4..f747ee1f96d7 100644
--- a/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
+++ b/drivers/gpu/drm/i915/gt/gen6_ppgtt.c
@@ -259,6 +259,9 @@ static void gen6_ppgtt_free_pd(struct gen6_ppgtt *ppgtt)
        struct i915_page_table *pt;
        u32 pde;
 
+       if (!pd)
+               return;
+
        gen6_for_all_pdes(pt, pd, pde)
                if (pt)
                        free_pt(&ppgtt->base.vm, pt);
@@ -427,6 +430,7 @@ void gen6_ppgtt_unpin(struct i915_ppgtt *base)
 
 struct i915_ppgtt *gen6_ppgtt_create(struct intel_gt *gt)
 {
+       struct i915_page_directory *pd;
        struct i915_ggtt * const ggtt = gt->ggtt;
        struct gen6_ppgtt *ppgtt;
        int err;
@@ -455,11 +459,12 @@ struct i915_ppgtt *gen6_ppgtt_create(struct intel_gt *gt)
        if (err)
                goto err_put;
 
-       ppgtt->base.pd = gen6_alloc_top_pd(ppgtt);
-       if (IS_ERR(ppgtt->base.pd)) {
-               err = PTR_ERR(ppgtt->base.pd);
+       pd = gen6_alloc_top_pd(ppgtt);
+       if (IS_ERR(pd)) {
+               err = PTR_ERR(pd);
                goto err_put;
        }
+       ppgtt->base.pd = pd;
 
        return &ppgtt->base;
 
-- 
2.25.1

Reply via email to