drm_gem_objects_lookup() may allocate the output array and acquire
references on GEM objects before returning an error. This requires
callers to handle partial cleanup correctly, which is not obvious and
easy to get wrong.

Introduce drm_gem_objects_lookup_safe(), a wrapper helper that
guarantees cleanup on failure. If lookup fails, the helper drops any
acquired object references, frees the array, and sets the output pointer
to NULL.

This provides a safer alternative for new and fragile call sites without
changing the behavior of drm_gem_objects_lookup() or affecting existing
callers.

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]>
Change-Id: I553551dd1e7aadf1b2a477eaf19ce9c80b2ce2ea
---
 drivers/gpu/drm/drm_gem.c | 48 +++++++++++++++++++++++++++++++++++++++
 include/drm/drm_gem.h     |  2 ++
 2 files changed, 50 insertions(+)

diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 0f8013b9377e..f1d13700a62c 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -830,6 +830,54 @@ int drm_gem_objects_lookup(struct drm_file *filp, void 
__user *bo_handles,
 }
 EXPORT_SYMBOL(drm_gem_objects_lookup);
 
+/**
+ * drm_gem_objects_lookup_safe - look up GEM objects from an array of handles 
with failure cleanup
+ * @filp: DRM file private data
+ * @bo_handles: user pointer to array of userspace handles
+ * @count: size of handle array
+ * @objs_out: returned pointer to array of drm_gem_object pointers
+ *
+ * Wrapper around drm_gem_objects_lookup() that guarantees cleanup on failure.
+ *
+ * On success, *@objs_out is set to an allocated array of @count pointers
+ * containing GEM objects. The caller must drop references with
+ * drm_gem_object_put() and free the array with kvfree().
+ *
+ * On failure, this helper will drm_gem_object_put() any successfully looked up
+ * objects, free the array, and set *@objs_out to NULL.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_objects_lookup_safe(struct drm_file *filp, void __user *bo_handles,
+                               int count, struct drm_gem_object ***objs_out)
+{
+       struct drm_gem_object **objs;
+       int ret, i;
+
+       /* Ensure callers never see stale pointers on failure. */
+       *objs_out = NULL;
+
+       ret = drm_gem_objects_lookup(filp, bo_handles, count, objs_out);
+       if (!ret)
+               return 0;
+
+       objs = *objs_out;
+       if (!objs)
+               return ret;
+
+       for (i = 0; i < count; i++) {
+               if (objs[i])
+                       drm_gem_object_put(objs[i]);
+       }
+
+       kvfree(objs);
+       *objs_out = NULL;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(drm_gem_objects_lookup_safe);
+
 /**
  * drm_gem_object_lookup - look up a GEM object from its handle
  * @filp: DRM file private date
diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h
index 8d48d2af2649..7decce2fdef7 100644
--- a/include/drm/drm_gem.h
+++ b/include/drm/drm_gem.h
@@ -561,6 +561,8 @@ void drm_gem_vunmap(struct drm_gem_object *obj, struct 
iosys_map *map);
 
 int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles,
                           int count, struct drm_gem_object ***objs_out);
+int drm_gem_objects_lookup_safe(struct drm_file *filp, void __user *bo_handles,
+                               int count, struct drm_gem_object ***objs_out);
 struct drm_gem_object *drm_gem_object_lookup(struct drm_file *filp, u32 
handle);
 long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle,
                                    bool wait_all, unsigned long timeout);
-- 
2.34.1

Reply via email to