[PATCH v3] drm: Fix drm_pending_vblank_event leak in error path for out_fence_ptr

Thadeu Lima de Souza Cascardo <[email protected]>
Newsgroups org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel
Message-ID <20260817-drm_pending_vblank_event_leak-v3-1-7582b24447d0@igalia.com>
When an out_fence_ptr is provided but DRM_MODE_PAGE_FLIP_EVENT is not
set, a drm_pending_vblank_event will be allocated. If later, there is an
allocation failure or another failure at setup_out_fence(), that event
will not have base.fence set and it will not be released at
complete_signaling().

Release the event and set crtc_state->event to NULL just like in the
DRM_MODE_PAGE_FLIP_EVENT case when there is a failure at
drm_event_reserve_init(). That is, prepare_signaling() releases the
event and there is nothing to be done at complete_signaling(). Use
drm_event_cancel_free() as that will also undo drm_event_reserve_init()
in case it has been called.

Reported-by: [email protected]
Closes: https://sashiko.dev/#/patchset/20260727-drm_crtc_atomic_commit_leak-v1-1-23d9948a9d7c@igalia.com?part=1
Fixes: 92c715fca907 ("drm/atomic: Fix double free in drm_atomic_state_default_clear")
Signed-off-by: Thadeu Lima de Souza Cascardo <[email protected]>
---
Changes in v3:
- Use a label for the common exit pattern.
- Link to v2: https://patch.msgid.link/20260729-drm_pending_vblank_event_leak-v2-1-a5074aae07df@igalia.com

Changes in v2:
- Fix UAF when DRM_MODE_PAGE_FLIP_EVENT is used.
- Link to v1: https://patch.msgid.link/20260728-drm_pending_vblank_event_leak-v1-1-08429b920b16@igalia.com
---
 drivers/gpu/drm/drm_atomic_uapi.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 1050dddadb17..0aba33824595 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -1426,6 +1426,7 @@ static int prepare_signaling(struct drm_device *dev,
 	struct drm_crtc_state *crtc_state;
 	struct drm_connector *conn;
 	struct drm_connector_state *conn_state;
+	struct drm_pending_vblank_event *e;
 	int i, c = 0, ret;
 
 	if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)
@@ -1437,8 +1438,6 @@ static int prepare_signaling(struct drm_device *dev,
 		fence_ptr = get_out_fence_for_crtc(crtc_state->state, crtc);
 
 		if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT || fence_ptr) {
-			struct drm_pending_vblank_event *e;
-
 			e = create_vblank_event(crtc, arg->user_data);
 			if (!e)
 				return -ENOMEM;
@@ -1447,7 +1446,7 @@ static int prepare_signaling(struct drm_device *dev,
 		}
 
 		if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
-			struct drm_pending_vblank_event *e = crtc_state->event;
+			e = crtc_state->event;
 
 			if (!file_priv)
 				continue;
@@ -1464,11 +1463,14 @@ static int prepare_signaling(struct drm_device *dev,
 		if (fence_ptr) {
 			struct dma_fence *fence;
 			struct drm_out_fence_state *f;
+			e = crtc_state->event;
+
+			ret = -ENOMEM;
 
 			f = krealloc(*fence_state, sizeof(**fence_state) *
 				     (*num_fences + 1), GFP_KERNEL);
 			if (!f)
-				return -ENOMEM;
+				goto error;
 
 			memset(&f[*num_fences], 0, sizeof(*f));
 
@@ -1477,12 +1479,12 @@ static int prepare_signaling(struct drm_device *dev,
 
 			fence = drm_crtc_create_fence(crtc);
 			if (!fence)
-				return -ENOMEM;
+				goto error;
 
 			ret = setup_out_fence(&f[(*num_fences)++], fence);
 			if (ret) {
 				dma_fence_put(fence);
-				return ret;
+				goto error;
 			}
 
 			crtc_state->event->base.fence = fence;
@@ -1538,6 +1540,11 @@ static int prepare_signaling(struct drm_device *dev,
 	}
 
 	return 0;
+
+error:
+	drm_event_cancel_free(dev, &e->base);
+	crtc_state->event = NULL;
+	return ret;
 }
 
 static void complete_signaling(struct drm_device *dev,

---
base-commit: 5d2584e2ffaabf2d420edcb2d120209e151f8dce
change-id: 20260728-drm_pending_vblank_event_leak-a36cedb296ba

Best regards,
--  
Thadeu Lima de Souza Cascardo <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.