drm_gem_objects_lookup() can allocate the output array and take
references on GEM objects before it fails.

If an error happens part-way through, callers previously had to clean up
partially created results themselves. This relied on subtle and
undocumented behavior and was easy to get wrong.

Make drm_gem_objects_lookup() clean up on failure. The function now
drops any references it already took, frees the array, and sets
*objs_out to NULL before returning an error.

On success, behavior is unchanged. Existing callers remain correct and
their error cleanup paths simply do nothing when *objs_out is NULL.

v3: Move partial-lookup cleanup into objects_lookup(), perform reference
dropping outside the lock, and remove reliance on __GFP_ZERO or implicit
NULL handling. (Christian)

Suggested-by: Christian König <[email protected]>
Suggested-by: Tvrtko Ursulin <[email protected]>
Signed-off-by: Srinivasan Shanmugam <[email protected]>
---
 drivers/gpu/drm/drm_gem.c | 37 +++++++++++++++++++++++++------------
 1 file changed, 25 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index a1a9c828938b..da18e49a8bde 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -759,6 +759,11 @@ static int objects_lookup(struct drm_file *filp, u32 
*handle, int count,
        }
        spin_unlock(&filp->table_lock);
 
+       if (ret) {
+               while (--i >= 0)
+                       drm_gem_object_put(objs[i]);
+       }
+
        return ret;
 }
 
@@ -775,9 +780,11 @@ static int objects_lookup(struct drm_file *filp, u32 
*handle, int count,
  * For a single handle lookup, use drm_gem_object_lookup().
  *
  * Returns:
- * @objs filled in with GEM object pointers. Returned GEM objects need to be
- * released with drm_gem_object_put(). -ENOENT is returned on a lookup
- * failure. 0 is returned on success.
+ * On success, *@objs_out is set to an allocated array of @count pointers
+ * containing GEM objects. The caller must drop the references with
+ * drm_gem_object_put() and free the array with kvfree().
+ *
+ * On failure, *@objs_out is set to NULL and no further action is required.
  *
  */
 int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles,
@@ -785,36 +792,42 @@ int drm_gem_objects_lookup(struct drm_file *filp, void 
__user *bo_handles,
 {
        struct drm_device *dev = filp->minor->dev;
        struct drm_gem_object **objs;
-       u32 *handles;
+       u32 *handles = NULL;
        int ret;
 
+       *objs_out = NULL;
        if (!count)
                return 0;
 
-       objs = kvmalloc_array(count, sizeof(struct drm_gem_object *),
-                            GFP_KERNEL | __GFP_ZERO);
+       objs = kvmalloc_array(count, sizeof(*objs), GFP_KERNEL);
        if (!objs)
                return -ENOMEM;
 
-       *objs_out = objs;
-
        handles = kvmalloc_array(count, sizeof(u32), GFP_KERNEL);
        if (!handles) {
                ret = -ENOMEM;
-               goto out;
+               goto err_free_objs;
        }
 
        if (copy_from_user(handles, bo_handles, count * sizeof(u32))) {
                ret = -EFAULT;
                drm_dbg_core(dev, "Failed to copy in GEM handles\n");
-               goto out;
+               goto err_free_handles;
        }
 
        ret = objects_lookup(filp, handles, count, objs);
-out:
        kvfree(handles);
-       return ret;
+       if (ret)
+               goto err_free_objs;
 
+       *objs_out = objs;
+       return 0;
+
+err_free_handles:
+       kvfree(handles);
+err_free_objs:
+       kvfree(objs);
+       return ret;
 }
 EXPORT_SYMBOL(drm_gem_objects_lookup);
 
-- 
2.34.1

Reply via email to