drm_atomic_private_obj_init() initializes the modeset lock and adds the
object to the privobj_list before calling atomic_create_state().

However, if atomic_create_state() fails, the function returns the error
directly without removing the object from the list or cleaning up the
lock. This leaves a dangling entry in privobj_list and a leaked lock.

Fix this by unwinding both operations in an error path.

Fixes: b962a12050a3 ("drm/atomic: integrate modeset lock with private objects")
Suggested-by: Laurent Pinchart <[email protected]>
Reviewed-by: Maarten Lankhorst <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
 drivers/gpu/drm/drm_atomic.c | 29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 9bb761eda59f..8f9b17395b90 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1030,6 +1030,15 @@ int drm_atomic_private_obj_init(struct drm_device *dev,
 {
        struct drm_private_state *state;
+       int ret;
+
        memset(obj, 0, sizeof(*obj));
 
+       /*
+        * Keep the list node in a state drm_atomic_private_obj_fini()
+        * can safely handle if we fail before adding it to the private
+        * objects list.
+        */
+       INIT_LIST_HEAD(&obj->head);
+
        drm_modeset_lock_init(&obj->lock);
 
@@ -1039,10 +1048,17 @@ int drm_atomic_private_obj_init(struct drm_device *dev,
 
        state = obj->funcs->atomic_create_state(obj);
-       if (IS_ERR(state))
-               return PTR_ERR(state);
+       if (IS_ERR(state)) {
+               ret = PTR_ERR(state);
+               goto err_list_del;
+       }
 
        obj->state = state;
 
        return 0;
+
+err_list_del:
+       list_del_init(&obj->head);
+       drm_modeset_lock_fini(&obj->lock);
+       return ret;
 }
 EXPORT_SYMBOL(drm_atomic_private_obj_init);
@@ -1057,4 +1073,13 @@ void
 drm_atomic_private_obj_fini(struct drm_private_obj *obj)
 {
+       /*
+        * Some callers ignore drm_atomic_private_obj_init() failures
+        * and unconditionally register a teardown action that calls
+        * this function. Make it idempotent so a failed init followed
+        * by fini does not cause a double list_del.
+        */
+       if (list_empty(&obj->head))
+               return;
+
        list_del(&obj->head);
        obj->funcs->atomic_destroy_state(obj, obj->state);

-- 
2.55.0

Reply via email to