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