diff options
| author | Furkan Sahin <furkan-dev@proton.me> | 2025-03-21 18:35:36 +0100 |
|---|---|---|
| committer | Furkan Sahin <furkan-dev@proton.me> | 2025-03-21 18:35:36 +0100 |
| commit | f9b92b4668cbffe3efc75a04dd1bae415e7077ad (patch) | |
| tree | eae246cd4915db70ccc8160f112f2a29e460a64d | |
| parent | 4fa27dd69b2cfcf2858c59686ea6e627aea6909d (diff) | |
server: recreate renderer in idle callback to avoid UAF
Destroying the wlr_renderer in a callback to its own renderer_lost event
is unsafe due to wl_signal_emit*() still accessing it after it was
destroyed.
Delegate recreation of renderer to an idle callback and ensure that only
one such idle callback is scheduled at a time by storing the returned
event source.
| -rw-r--r-- | include/sway/server.h | 1 | ||||
| -rw-r--r-- | sway/server.c | 18 |
2 files changed, 16 insertions, 3 deletions
diff --git a/include/sway/server.h b/include/sway/server.h index feb516c5..b1d7523c 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -46,6 +46,7 @@ struct sway_server { struct wl_listener new_output; struct wl_listener renderer_lost; + struct wl_event_source *recreating_renderer; struct wlr_idle_notifier_v1 *idle_notifier_v1; struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; diff --git a/sway/server.c b/sway/server.c index ff7ed42e..9d882862 100644 --- a/sway/server.c +++ b/sway/server.c @@ -182,11 +182,11 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) { drmFreeVersion(version); } -static void handle_renderer_lost(struct wl_listener *listener, void *data) { - struct sway_server *server = wl_container_of(listener, server, renderer_lost); +static void do_renderer_recreate(void *data) { + struct sway_server *server = data; + server->recreating_renderer = NULL; sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); - struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); if (renderer == NULL) { sway_log(SWAY_ERROR, "Unable to create renderer"); @@ -221,6 +221,18 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { wlr_renderer_destroy(old_renderer); } +static void handle_renderer_lost(struct wl_listener *listener, void *data) { + struct sway_server *server = wl_container_of(listener, server, renderer_lost); + + if (server->recreating_renderer != NULL) { + sway_log(SWAY_DEBUG, "Re-creation of renderer already scheduled"); + return; + } + + sway_log(SWAY_INFO, "Scheduling re-creation of renderer after GPU reset"); + server->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server); +} + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); |
