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
