wlroots/xwayland/xwayland.c
Simon Ser 8714657d7b xwayland: unset wlr_xwayland.server on destroy
This fixes a use-after-free in the Sway patch to filter the Xwayland
shell [1].

The server is destroyed first, then the shell. The Xwayland process
might still be using the shell while running.

When the shell is destroyed, libwayland will invoke the global
filter (to figure out whether to send a wl_registry.global_remove
to clients). Then Sway will compare the client with
wlr_xwayland_server.client. However, at that point, the server is
gone.

Reset the server to NULL so that Sway can check whether the server
is still running.

[1]: https://github.com/swaywm/sway/pull/7647
2023-06-22 16:28:43 +02:00

181 lines
4.8 KiB
C

#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/log.h>
#include <wlr/xwayland/shell.h>
#include <wlr/xwayland/xwayland.h>
#include "sockets.h"
#include "xwayland/xwm.h"
struct wlr_xwayland_cursor {
uint8_t *pixels;
uint32_t stride;
uint32_t width;
uint32_t height;
int32_t hotspot_x;
int32_t hotspot_y;
};
static void handle_server_destroy(struct wl_listener *listener, void *data) {
struct wlr_xwayland *xwayland =
wl_container_of(listener, xwayland, server_destroy);
// Server is being destroyed so avoid destroying it once again.
xwayland->server = NULL;
wlr_xwayland_destroy(xwayland);
}
static void handle_server_start(struct wl_listener *listener, void *data) {
struct wlr_xwayland *xwayland =
wl_container_of(listener, xwayland, server_start);
wlr_xwayland_shell_v1_set_client(xwayland->shell_v1, xwayland->server->client);
}
static void handle_server_ready(struct wl_listener *listener, void *data) {
struct wlr_xwayland *xwayland =
wl_container_of(listener, xwayland, server_ready);
struct wlr_xwayland_server_ready_event *event = data;
xwayland->xwm = xwm_create(xwayland, event->wm_fd);
if (!xwayland->xwm) {
return;
}
if (xwayland->seat) {
xwm_set_seat(xwayland->xwm, xwayland->seat);
}
if (xwayland->cursor != NULL) {
struct wlr_xwayland_cursor *cur = xwayland->cursor;
xwm_set_cursor(xwayland->xwm, cur->pixels, cur->stride, cur->width,
cur->height, cur->hotspot_x, cur->hotspot_y);
}
wl_signal_emit_mutable(&xwayland->events.ready, NULL);
}
void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) {
if (!xwayland) {
return;
}
wl_list_remove(&xwayland->server_destroy.link);
wl_list_remove(&xwayland->server_start.link);
wl_list_remove(&xwayland->server_ready.link);
free(xwayland->cursor);
wlr_xwayland_set_seat(xwayland, NULL);
wlr_xwayland_server_destroy(xwayland->server);
xwayland->server = NULL;
wlr_xwayland_shell_v1_destroy(xwayland->shell_v1);
free(xwayland);
}
struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display,
struct wlr_compositor *compositor, bool lazy) {
struct wlr_xwayland *xwayland = calloc(1, sizeof(struct wlr_xwayland));
if (!xwayland) {
return NULL;
}
xwayland->wl_display = wl_display;
xwayland->compositor = compositor;
wl_signal_init(&xwayland->events.new_surface);
wl_signal_init(&xwayland->events.ready);
wl_signal_init(&xwayland->events.remove_startup_info);
xwayland->shell_v1 = wlr_xwayland_shell_v1_create(wl_display, 1);
if (xwayland->shell_v1 == NULL) {
free(xwayland);
return NULL;
}
struct wlr_xwayland_server_options options = {
.lazy = lazy,
.enable_wm = true,
#if HAVE_XCB_XFIXES_SET_CLIENT_DISCONNECT_MODE
.terminate_delay = lazy ? 10 : 0,
#endif
};
xwayland->server = wlr_xwayland_server_create(wl_display, &options);
if (xwayland->server == NULL) {
wlr_xwayland_shell_v1_destroy(xwayland->shell_v1);
free(xwayland);
return NULL;
}
xwayland->display_name = xwayland->server->display_name;
xwayland->server_destroy.notify = handle_server_destroy;
wl_signal_add(&xwayland->server->events.destroy, &xwayland->server_destroy);
xwayland->server_start.notify = handle_server_start;
wl_signal_add(&xwayland->server->events.start, &xwayland->server_start);
xwayland->server_ready.notify = handle_server_ready;
wl_signal_add(&xwayland->server->events.ready, &xwayland->server_ready);
return xwayland;
}
void wlr_xwayland_set_cursor(struct wlr_xwayland *xwayland,
uint8_t *pixels, uint32_t stride, uint32_t width, uint32_t height,
int32_t hotspot_x, int32_t hotspot_y) {
if (xwayland->xwm != NULL) {
xwm_set_cursor(xwayland->xwm, pixels, stride, width, height,
hotspot_x, hotspot_y);
return;
}
free(xwayland->cursor);
xwayland->cursor = calloc(1, sizeof(struct wlr_xwayland_cursor));
if (xwayland->cursor == NULL) {
return;
}
xwayland->cursor->pixels = pixels;
xwayland->cursor->stride = stride;
xwayland->cursor->width = width;
xwayland->cursor->height = height;
xwayland->cursor->hotspot_x = hotspot_x;
xwayland->cursor->hotspot_y = hotspot_y;
}
static void xwayland_handle_seat_destroy(struct wl_listener *listener,
void *data) {
struct wlr_xwayland *xwayland =
wl_container_of(listener, xwayland, seat_destroy);
wlr_xwayland_set_seat(xwayland, NULL);
}
void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland,
struct wlr_seat *seat) {
if (xwayland->seat) {
wl_list_remove(&xwayland->seat_destroy.link);
}
xwayland->seat = seat;
if (xwayland->xwm) {
xwm_set_seat(xwayland->xwm, seat);
}
if (seat == NULL) {
return;
}
xwayland->seat_destroy.notify = xwayland_handle_seat_destroy;
wl_signal_add(&seat->events.destroy, &xwayland->seat_destroy);
}