From 6e5d1e00e02cf9086224b5eb3258f901a26de413 Mon Sep 17 00:00:00 2001 From: Dery Almas Date: Tue, 3 Feb 2026 16:26:56 +0100 Subject: [PATCH] Wayland Embedder: Fix FD leak with inert objects Freshly deleted objects are temporarily "inert" until their destruction is acknowledged by the compositor. Inert objects are ignored. By doing so, we mistakenly returned too early and missed some FD cleanup logic. This patch ensures that any outstanding FDs are always closed by moving its logic outside of the message handling routine. --- .../linuxbsd/wayland/wayland_embedder.cpp | 45 +++++++++---------- platform/linuxbsd/wayland/wayland_embedder.h | 2 +- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/platform/linuxbsd/wayland/wayland_embedder.cpp b/platform/linuxbsd/wayland/wayland_embedder.cpp index bab5027bc79..41e6e48ced1 100644 --- a/platform/linuxbsd/wayland/wayland_embedder.cpp +++ b/platform/linuxbsd/wayland/wayland_embedder.cpp @@ -2351,13 +2351,10 @@ void WaylandEmbedder::shutdown() { } } -Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, int *fds_requested) { +Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, LocalVector &r_sent_fds) { ERR_FAIL_NULL_V(info, ERR_BUG); - ERR_FAIL_NULL_V(fds_requested, ERR_BUG); ERR_FAIL_NULL_V_MSG(info->direction == ProxyDirection::COMPOSITOR && client, ERR_BUG, "Wait, where did this message come from?"); - *fds_requested = 0; - WaylandObject *object = nullptr; uint32_t global_id = INVALID_ID; @@ -2410,17 +2407,15 @@ Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *in } ERR_FAIL_NULL_V(message, ERR_BUG); - *fds_requested = String(message->signature).count("h"); - LocalVector sent_fds; - - if (*fds_requested > 0) { - DEBUG_LOG_WAYLAND_EMBED(vformat("Requested %d FDs.", *fds_requested)); + int fds_requested = String(message->signature).count("h"); + if (fds_requested > 0) { + DEBUG_LOG_WAYLAND_EMBED(vformat("Requested %d FDs.", fds_requested)); List &fd_queue = info->direction == ProxyDirection::COMPOSITOR ? client->fds : compositor_fds; - for (int i = 0; i < *fds_requested; ++i) { + for (int i = 0; i < fds_requested; ++i) { ERR_FAIL_COND_V_MSG(fd_queue.is_empty(), ERR_BUG, "Out of FDs."); DEBUG_LOG_WAYLAND_EMBED(vformat("Fetching FD %d.", fd_queue.front()->get())); - sent_fds.push_back(fd_queue.front()->get()); + r_sent_fds.push_back(fd_queue.front()->get()); fd_queue.pop_front(); } @@ -2429,6 +2424,7 @@ Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *in if (object->destroyed) { DEBUG_LOG_WAYLAND_EMBED("Ignoring message for inert object."); + // Inert object. return OK; } @@ -2451,7 +2447,7 @@ Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *in DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler."); if (handle_generic_msg(client, object, message, info, buf)) { - send_raw_message(compositor_socket, { { buf, info->size } }, sent_fds); + send_raw_message(compositor_socket, { { buf, info->size } }, r_sent_fds); } } else { uint32_t global_name = 0; @@ -2522,7 +2518,7 @@ Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *in buf[0] = instance_id; if (handle_generic_msg(&c, local_obj.get(), message, info, buf, instance_id)) { - send_raw_message(c.socket, { { buf, info->size } }, sent_fds); + send_raw_message(c.socket, { { buf, info->size } }, r_sent_fds); } handled = true; @@ -2558,7 +2554,7 @@ Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *in DEBUG_LOG_WAYLAND_EMBED("Falling back to generic handler."); if (handle_generic_msg(&c, local_obj.get(), message, info, buf)) { - send_raw_message(c.socket, { { buf, info->size } }, sent_fds); + send_raw_message(c.socket, { { buf, info->size } }, r_sent_fds); } handled = true; @@ -2594,7 +2590,7 @@ Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *in buf[0] = local_id; if (handle_generic_msg(client, local_obj.get(), message, info, buf)) { - send_raw_message(client->socket, { { buf, info->size } }, sent_fds); + send_raw_message(client->socket, { { buf, info->size } }, r_sent_fds); } } else { WARN_PRINT_ONCE(vformat("[Wayland Embedder] Unexpected client-less event from %s#g0x%x. Object has probably leaked.", object->interface->name, global_id)); @@ -2603,11 +2599,6 @@ Error WaylandEmbedder::handle_msg_info(Client *client, const struct msg_info *in } } - for (int fd : sent_fds) { - DEBUG_LOG_WAYLAND_EMBED(vformat("Closing fd %d.", fd)); - close(fd); - } - return OK; } @@ -2730,8 +2721,6 @@ Error WaylandEmbedder::handle_sock(int p_fd) { full_msg.msg_control = nullptr; full_msg.msg_controllen = 0; - int fds_requested = 0; - Client *client = nullptr; if (p_fd == compositor_socket) { // Let's figure out the recipient of the message. @@ -2747,12 +2736,20 @@ Error WaylandEmbedder::handle_sock(int p_fd) { client = &clients[p_fd]; } - if (handle_msg_info(client, &info, msg_buf.ptr(), &fds_requested) != OK) { - return ERR_BUG; + LocalVector sent_fds; + Error err = handle_msg_info(client, &info, msg_buf.ptr(), sent_fds); + + for (int fd : sent_fds) { + DEBUG_LOG_WAYLAND_EMBED(vformat("Closing fd %d.", fd)); + close(fd); } DEBUG_LOG_WAYLAND_EMBED(" === END PACKET === "); + if (err != OK) { + return ERR_BUG; + } + return OK; } diff --git a/platform/linuxbsd/wayland/wayland_embedder.h b/platform/linuxbsd/wayland/wayland_embedder.h index 62861d96744..67f92fe0930 100644 --- a/platform/linuxbsd/wayland/wayland_embedder.h +++ b/platform/linuxbsd/wayland/wayland_embedder.h @@ -610,7 +610,7 @@ class WaylandEmbedder { void shutdown(); bool handle_generic_msg(Client *client, const WaylandObject *p_object, const struct wl_message *message, const struct msg_info *info, uint32_t *buf, uint32_t instance_id = INVALID_ID); - Error handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, int *fds_requested); + Error handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, LocalVector &r_fds_requested); Error handle_sock(int p_fd); void handle_fd(int p_fd, int p_revents);