mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-03-07 04:34:24 -05:00
Fix use-after-free crash in cursor surface handling
### Problem `setcursor()` stores the client-provided `wlr_surface` pointer in `last_cursor.surface`, but never registers a destroy listener on it. When the client exits (e.g. closing a launcher like fuzzel), the surface is destroyed, but `last_cursor.surface` still holds the stale pointer. If the cursor hide timeout fires while the cursor surface is alive, and the client then exits, the next mouse movement calls `handlecursoractivity()`, which passes the dangling pointer to `wlr_cursor_set_surface()`. This causes a SIGSEGV in `wl_list_insert()` inside libwayland-server, as the `wl_list` embedded in the destroyed surface struct has been freed. A secondary issue exists in `setcursorshape()`: when a client switches from a custom cursor surface to a shape cursor, `last_cursor.surface` is set to NULL but the destroy listener (if registered) is not removed, leaving a dangling listener on the destroyed surface. The crash only manifests when `cursor_hidden` is true at the moment of the mouse movement, which is why it is intermittent and difficult to reproduce. ### Root cause Confirmed via `coredumpctl debug` and `bt full`: ``` #0 wl_list_insert (libwayland-server.so) #1 wlr_cursor_set_surface (libwlroots) #2 handlecursoractivity (mango.c) #3 motionnotify (mango.c) #4 motionrelative (mango.c) #5 wl_signal_emit_mutable #6 handle_libinput_readable ``` ### Fix - Add a `wl_listener` (`last_cursor_surface_destroy_listener`) that clears `last_cursor.surface` and removes itself when the surface is destroyed. - Initialize the listener's link in `setup()` so `wl_list_empty()` checks are reliable from the start. - In `setcursor()`, remove any existing listener before registering a new one on the incoming surface. - In `setcursorshape()`, remove the destroy listener when switching to a shape cursor. - Add a NULL guard in `handlecursoractivity()` as a safety net.
This commit is contained in:
parent
4b35b3b4a9
commit
9a43368279
1 changed files with 28 additions and 1 deletions
29
src/mango.c
29
src/mango.c
|
|
@ -935,6 +935,15 @@ static struct {
|
|||
int32_t hotspot_y;
|
||||
} last_cursor;
|
||||
|
||||
static void last_cursor_surface_destroy(struct wl_listener *listener, void *data) {
|
||||
last_cursor.surface = NULL;
|
||||
wl_list_remove(&listener->link);
|
||||
wl_list_init(&listener->link);
|
||||
}
|
||||
static struct wl_listener last_cursor_surface_destroy_listener = {
|
||||
.notify = last_cursor_surface_destroy
|
||||
};
|
||||
|
||||
#include "client/client.h"
|
||||
#include "config/preset.h"
|
||||
|
||||
|
|
@ -2180,6 +2189,11 @@ void setcursorshape(struct wl_listener *listener, void *data) {
|
|||
* actually has pointer focus first. If so, we can tell the cursor to
|
||||
* use the provided cursor shape. */
|
||||
if (event->seat_client == seat->pointer_state.focused_client) {
|
||||
/* Remove surface destroy listener if active */
|
||||
if (!wl_list_empty(&last_cursor_surface_destroy_listener.link))
|
||||
wl_list_remove(&last_cursor_surface_destroy_listener.link);
|
||||
wl_list_init(&last_cursor_surface_destroy_listener.link);
|
||||
|
||||
last_cursor.shape = event->shape;
|
||||
last_cursor.surface = NULL;
|
||||
if (!cursor_hidden)
|
||||
|
|
@ -4932,10 +4946,21 @@ void setcursor(struct wl_listener *listener, void *data) {
|
|||
* hardware cursor on the output that it's currently on and continue to
|
||||
* do so as the cursor moves between outputs. */
|
||||
if (event->seat_client == seat->pointer_state.focused_client) {
|
||||
/* Clear previous surface destroy listener if any */
|
||||
if (!wl_list_empty(&last_cursor_surface_destroy_listener.link))
|
||||
wl_list_remove(&last_cursor_surface_destroy_listener.link);
|
||||
wl_list_init(&last_cursor_surface_destroy_listener.link);
|
||||
|
||||
last_cursor.shape = 0;
|
||||
last_cursor.surface = event->surface;
|
||||
last_cursor.hotspot_x = event->hotspot_x;
|
||||
last_cursor.hotspot_y = event->hotspot_y;
|
||||
|
||||
/* Track surface destruction to avoid dangling pointer */
|
||||
if (event->surface)
|
||||
wl_signal_add(&event->surface->events.destroy,
|
||||
&last_cursor_surface_destroy_listener);
|
||||
|
||||
if (!cursor_hidden)
|
||||
wlr_cursor_set_surface(cursor, event->surface, event->hotspot_x,
|
||||
event->hotspot_y);
|
||||
|
|
@ -5398,6 +5423,8 @@ void handle_print_status(struct wl_listener *listener, void *data) {
|
|||
|
||||
void setup(void) {
|
||||
|
||||
wl_list_init(&last_cursor_surface_destroy_listener.link);
|
||||
|
||||
setenv("XCURSOR_SIZE", "24", 1);
|
||||
setenv("XDG_CURRENT_DESKTOP", "mango", 1);
|
||||
|
||||
|
|
@ -5841,7 +5868,7 @@ void handlecursoractivity(void) {
|
|||
if (last_cursor.shape)
|
||||
wlr_cursor_set_xcursor(cursor, cursor_mgr,
|
||||
wlr_cursor_shape_v1_name(last_cursor.shape));
|
||||
else
|
||||
else if (last_cursor.surface)
|
||||
wlr_cursor_set_surface(cursor, last_cursor.surface,
|
||||
last_cursor.hotspot_x, last_cursor.hotspot_y);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue