On 2/5/26 15:32, Srinivasan Shanmugam wrote:
> 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)
> 
> v4: Use goto-style error handling in objects_lookup(), drop partial
> references outside the lock, and simplify drm_gem_objects_lookup()
> cleanup by routing failures through err_free_handles as suggested.
> (Christian)
> 
> Cc: Alex Deucher <[email protected]>
> 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 | 48 ++++++++++++++++++++++++++-------------
>  1 file changed, 32 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
> index a1a9c828938b..e1218728352d 100644
> --- a/drivers/gpu/drm/drm_gem.c
> +++ b/drivers/gpu/drm/drm_gem.c
> @@ -750,14 +750,22 @@ static int objects_lookup(struct drm_file *filp, u32 
> *handle, int count,
>       for (i = 0; i < count; i++) {
>               /* Check if we currently have a reference on the object */
>               obj = idr_find(&filp->object_idr, handle[i]);
> -             if (!obj) {
> -                     ret = -ENOENT;
> -                     break;
> -             }
> +             if (!obj)
> +                     goto err;
> +
>               drm_gem_object_get(obj);
>               objs[i] = obj;
>       }
> +
>       spin_unlock(&filp->table_lock);
> +     return 0;
> +
> +err:
> +     ret = -ENOENT;

I think you can now also drop the ret local variable.

> +     spin_unlock(&filp->table_lock);
> +
> +     while (i--)
> +             drm_gem_object_put(objs[i]);
>  
>       return ret;
>  }
> @@ -775,9 +783,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 +795,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);

As pointed out by Tvrtko as well please rebase that on top of drm-misc-next. 
This code here has changed there.

Regards,
Christian.

>       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:
> +     if (ret)
> +             goto err_free_handles;
> +
>       kvfree(handles);
> -     return ret;
> +     *objs_out = objs;
> +     return 0;
>  
> +err_free_handles:
> +     kvfree(handles);
> +err_free_objs:
> +     kvfree(objs);
> +     return ret;
>  }
>  EXPORT_SYMBOL(drm_gem_objects_lookup);
>  

Reply via email to