Fix segfault due to use-after-free of wl_seat_listener (#2198)

Add some minor additional safety checks to wayland main_loop_wait. These come from kitty and are common in wl apps, so I kept them after debugging.

Signed-off-by: Tin Švagelj <tin.svagelj@live.com>
This commit is contained in:
Tin Švagelj
2025-05-11 15:11:06 +02:00
committed by GitHub
parent 03c4c5d7e8
commit 0c40ad9a02

View File

@@ -43,6 +43,7 @@
#include <wlr-layer-shell-client-protocol.h> #include <wlr-layer-shell-client-protocol.h>
#include <xdg-shell-client-protocol.h> #include <xdg-shell-client-protocol.h>
#include <cerrno>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@@ -333,7 +334,7 @@ static void output_description(void *data, struct wl_output *wl_output,
const char *description) {} const char *description) {}
#endif #endif
const struct wl_output_listener output_listener = { static const wl_output_listener output_listener = {
/*.geometry =*/output_geometry, /*.geometry =*/output_geometry,
/*.mode =*/output_mode, /*.mode =*/output_mode,
#ifdef WL_OUTPUT_DONE_SINCE_VERSION #ifdef WL_OUTPUT_DONE_SINCE_VERSION
@@ -379,7 +380,7 @@ void registry_handle_global(void *data, struct wl_registry *registry,
void registry_handle_global_remove(void *data, struct wl_registry *registry, void registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name) {} uint32_t name) {}
static const struct wl_registry_listener registry_listener = { static const wl_registry_listener registry_listener = {
registry_handle_global, registry_handle_global_remove}; registry_handle_global, registry_handle_global_remove};
static void layer_surface_configure(void *data, static void layer_surface_configure(void *data,
@@ -392,7 +393,7 @@ static void layer_surface_configure(void *data,
static void layer_surface_closed(void *data, static void layer_surface_closed(void *data,
struct zwlr_layer_surface_v1 *layer_surface) {} struct zwlr_layer_surface_v1 *layer_surface) {}
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { static const zwlr_layer_surface_v1_listener layer_surface_listener = {
/*.configure =*/&layer_surface_configure, /*.configure =*/&layer_surface_configure,
/*.closed =*/&layer_surface_closed, /*.closed =*/&layer_surface_closed,
}; };
@@ -516,6 +517,14 @@ void on_pointer_axis(void *data, struct wl_pointer *pointer, std::uint32_t time,
llua_mouse_hook(event); llua_mouse_hook(event);
} }
static const wl_pointer_listener pointer_listener = {
.enter = on_pointer_enter,
.leave = on_pointer_leave,
.motion = on_pointer_motion,
.button = on_pointer_button,
.axis = on_pointer_axis,
};
static void seat_capability_listener(void *data, wl_seat *seat, static void seat_capability_listener(void *data, wl_seat *seat,
uint32_t capability_int) { uint32_t capability_int) {
wl_seat_capability capabilities = wl_seat_capability capabilities =
@@ -524,19 +533,17 @@ static void seat_capability_listener(void *data, wl_seat *seat,
if ((capabilities & WL_SEAT_CAPABILITY_POINTER) > 0) { if ((capabilities & WL_SEAT_CAPABILITY_POINTER) > 0) {
wl_globals.pointer = wl_seat_get_pointer(seat); wl_globals.pointer = wl_seat_get_pointer(seat);
static wl_pointer_listener listener{ wl_pointer_add_listener(wl_globals.pointer, &pointer_listener, data);
.enter = on_pointer_enter,
.leave = on_pointer_leave,
.motion = on_pointer_motion,
.button = on_pointer_button,
.axis = on_pointer_axis,
};
wl_pointer_add_listener(wl_globals.pointer, &listener, data);
} }
} }
} }
static void seat_name_listener(void *data, struct wl_seat *wl_seat, static void seat_name_listener(void *data, struct wl_seat *wl_seat,
const char *name) {} const char *name) {}
static const wl_seat_listener seat_listener = {
.capabilities = seat_capability_listener,
.name = seat_name_listener,
};
#endif /* BUILD_MOUSE_EVENTS */ #endif /* BUILD_MOUSE_EVENTS */
bool display_output_wayland::initialize() { bool display_output_wayland::initialize() {
@@ -575,11 +582,7 @@ bool display_output_wayland::initialize() {
&layer_surface_listener, nullptr); &layer_surface_listener, nullptr);
#ifdef BUILD_MOUSE_EVENTS #ifdef BUILD_MOUSE_EVENTS
wl_seat_listener listener{ wl_seat_add_listener(wl_globals.seat, &seat_listener, global_window);
.capabilities = seat_capability_listener,
.name = seat_name_listener,
};
wl_seat_add_listener(wl_globals.seat, &listener, global_window);
#endif /* BUILD_MOUSE_EVENTS */ #endif /* BUILD_MOUSE_EVENTS */
wl_surface_commit(global_window->surface); wl_surface_commit(global_window->surface);
@@ -598,41 +601,55 @@ bool display_output_wayland::shutdown() { return false; }
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0])) #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0]))
static bool added = false;
bool display_output_wayland::main_loop_wait(double t) { bool display_output_wayland::main_loop_wait(double t) {
while (wl_display_prepare_read(global_display) != 0) errno = 0;
wl_display_dispatch_pending(global_display); while (wl_display_prepare_read(global_display) != 0) {
wl_display_flush(global_display); if (wl_display_dispatch_pending(global_display) == -1) {
CRIT_ERR("wayland error: %s", strerror(errno));
}
}
errno = 0;
if (wl_display_flush(global_display) < 0 && errno != EAGAIN) {
wl_display_cancel_read(global_display);
CRIT_ERR("wayland error: %s", strerror(errno));
}
if (t < 0.0) { t = 0.0; } if (t < 0.0) { t = 0.0; }
int ms = t * 1000; int ms = t * 1000;
/* add fd to epoll set the first time around */ /* add fd to epoll set the first time around */
if (!added) { static bool configured_epoll = false;
if (!configured_epoll) {
ep[0].events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLET; ep[0].events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLET;
ep[0].data.ptr = nullptr; ep[0].data.ptr = nullptr;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, wl_display_get_fd(global_display), if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, wl_display_get_fd(global_display),
&ep[0]) == -1) { &ep[0]) == -1) {
perror("conky: epoll_ctl: add"); CRIT_ERR("unable to setup epoll for display fd");
return false; return false;
} }
added = true; configured_epoll = true;
} }
/* wait for Wayland event or timeout */ /* wait for Wayland event or timeout */
int ep_count = epoll_wait(epoll_fd, ep, ARRAY_LENGTH(ep), ms); int ep_count = epoll_wait(epoll_fd, ep, ARRAY_LENGTH(ep), ms);
if (ep_count > 0) { if (ep_count > 0) {
if (ep[0].events & (EPOLLERR | EPOLLHUP)) { if (ep[0].events & (EPOLLERR | EPOLLHUP)) { CRIT_ERR("output closed"); }
NORM_ERR("output closed");
exit(1);
return false;
}
} }
wl_display_read_events(global_display); int read_status = 0;
if (ep_count > 0) {
read_status = wl_display_read_events(global_display);
} else {
wl_display_cancel_read(global_display);
}
wl_display_dispatch_pending(global_display); if (read_status == 0) {
int num = wl_display_dispatch_pending(global_display);
(void)num;
DBGP2("dispatched %d Wayland events", num);
}
wl_display_flush(global_display); wl_display_flush(global_display);