From 930f768bdb6ad66b5572f071acde63da71db6920 Mon Sep 17 00:00:00 2001 From: Ray Smith Date: Wed, 8 Oct 2025 17:18:57 +0100 Subject: [PATCH] backend-drm: Fix double-free of pending state in error path drm_pending_state_apply_atomic is supposed to leave pending_state untouched if called as a test-only and that test fails, as per the docs for drm_pending_state_test. If it gets as far as doing the atomic test and it fails, it doesn't free pending_state. However if it fails to even compile the state for the atomic test (for example, if a particular property is not implemented in the driver for a particular plane), it frees pending_state. In this case, drm_output_propose_state will free the contained drm_output_state again, typically leading to a segfault. Treat failing to compile the state for the atomic test in the same way as successfully running the atomic test but it failing. Signed-off-by: Ray Smith --- libweston/backend-drm/kms.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/libweston/backend-drm/kms.c b/libweston/backend-drm/kms.c index 76dcd5886..ef00ed47d 100644 --- a/libweston/backend-drm/kms.c +++ b/libweston/backend-drm/kms.c @@ -1625,7 +1625,10 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, if (ret != 0) { weston_log("atomic: couldn't compile atomic state\n"); - goto out; + if (mode == DRM_STATE_TEST_ONLY) + goto out_test_only; + else + goto out; } if (may_tear) tear_flag = DRM_MODE_PAGE_FLIP_ASYNC; @@ -1643,12 +1646,8 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, if (ret == 0) drm_pending_state_clear_tearing(pending_state); } - /* Test commits do not take ownership of the state; return - * without freeing here. */ - if (mode == DRM_STATE_TEST_ONLY) { - drmModeAtomicFree(req); - return ret; - } + if (mode == DRM_STATE_TEST_ONLY) + goto out_test_only; if (ret != 0) { wl_list_for_each(output_state, &pending_state->output_list, link) @@ -1669,8 +1668,11 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state, assert(wl_list_empty(&pending_state->output_list)); out: - drmModeAtomicFree(req); drm_pending_state_free(pending_state); + /* Test commits do not take ownership of the state; return + * without freeing here. */ +out_test_only: + drmModeAtomicFree(req); return ret; }