From 5494df621f692466bbecc9a7865ce7dd0cc4dd81 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 7 Sep 2026 15:23:44 +0200 Subject: [PATCH] drm/atomic: Only call atomic_destroy_state on a !NULL pointer The drm_atomic_commit structure is freed through the drm_atomic_commit_put() function, that eventually calls drm_atomic_commit_default_clear() by default when there's no active users of that state. It then iterates over all objects with a state, and will call the atomic_destroy_state callback on the state pointer. The state pointer is mostly used these days to point to which of the old or new state needs to be freed, depending on whether the state was committed or not. So it all makes sense. However, with the hardware state readout support approaching, we might have a state, with multiple objects in it, but no state to free because we want them to persist. In such a case, the state pointer is going to be NULL, and thus we'll end up with NULL pointer dereference. Test if the state pointer is non-NULL before calling atomic_destroy_state on it. Reviewed-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Link: https://lore.kernel.org/r/20260907-drm-state-readout-v5-8-5fa1ac7a5148@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_atomic.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 0ebbe10d497a..9bb761eda59f 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -293,8 +293,10 @@ void drm_atomic_commit_default_clear(struct drm_atomic_commit *state) if (!connector) continue; - connector->funcs->atomic_destroy_state(connector, - state->connectors[i].state_to_destroy); + if (state->connectors[i].state_to_destroy) + connector->funcs->atomic_destroy_state(connector, + state->connectors[i].state_to_destroy); + state->connectors[i].ptr = NULL; state->connectors[i].state_to_destroy = NULL; state->connectors[i].old_state = NULL; @@ -308,8 +310,9 @@ void drm_atomic_commit_default_clear(struct drm_atomic_commit *state) if (!crtc) continue; - crtc->funcs->atomic_destroy_state(crtc, - state->crtcs[i].state_to_destroy); + if (state->crtcs[i].state_to_destroy) + crtc->funcs->atomic_destroy_state(crtc, + state->crtcs[i].state_to_destroy); state->crtcs[i].ptr = NULL; state->crtcs[i].state_to_destroy = NULL; @@ -328,8 +331,10 @@ void drm_atomic_commit_default_clear(struct drm_atomic_commit *state) if (!plane) continue; - plane->funcs->atomic_destroy_state(plane, - state->planes[i].state_to_destroy); + if (state->planes[i].state_to_destroy) + plane->funcs->atomic_destroy_state(plane, + state->planes[i].state_to_destroy); + state->planes[i].ptr = NULL; state->planes[i].state_to_destroy = NULL; state->planes[i].old_state = NULL; @@ -356,8 +361,10 @@ void drm_atomic_commit_default_clear(struct drm_atomic_commit *state) if (!obj) continue; - obj->funcs->atomic_destroy_state(obj, - state->private_objs[i].state_to_destroy); + if (state->private_objs[i].state_to_destroy) + obj->funcs->atomic_destroy_state(obj, + state->private_objs[i].state_to_destroy); + state->private_objs[i].ptr = NULL; state->private_objs[i].state_to_destroy = NULL; state->private_objs[i].old_state = NULL;