commit 240a69ad63ad36893132ab1187035654d9478436
parent ab2e1f5817a8024366fcb02285c978c5fef7dae1
Author: Ferdinand Bachmann <ferdinand.bachmann@yrlf.at>
Date: Fri, 21 Mar 2025 18:35:36 +0100
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.
Diffstat:
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git 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
@@ -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();