From 5e4a1290ce75ed94e3f0f457d35a225f2ef3878c Mon Sep 17 00:00:00 2001
From: Olivier Fourdan <ofourdan@redhat.com>
Date: Tue, 28 Nov 2017 10:54:08 +0100
Subject: [PATCH] wayland: Avoid a race in wl_seat capabilities
The way wl_seat capabilities work, by notifying clients of capabilities
changes, and clients consequently requesting the relevant interface
objects (pointer, keyboard, touch) is inherently racy.
On quick VT changes for example, capabilities on the seat will be added
and removed, and by the time the client receives the capability change
notification and requests the relevant keyboard, pointer or touch,
another VT switch might have occurred and the wl_pointer, wl_keyboard or
wl_touch already destroyed, leading to a protocol error which kills the
client.
To avoid this, create the objects when requested regardless of the
capabilities.
Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1797
Related: https://bugzilla.gnome.org/show_bug.cgi?id=790932
---
src/wayland/meta-wayland-pointer.c | 45 ++++++++++++++++++++++++------
src/wayland/meta-wayland-seat.c | 9 ++----
src/wayland/meta-wayland-touch.c | 8 ------
3 files changed, 40 insertions(+), 22 deletions(-)
diff --git a/src/wayland/meta-wayland-pointer.c b/src/wayland/meta-wayland-pointer.c
index 3132abfd2..abd779ad7 100644
--- a/src/wayland/meta-wayland-pointer.c
+++ b/src/wayland/meta-wayland-pointer.c
@@ -109,7 +109,7 @@ meta_wayland_pointer_client_new (void)
}
static void
-meta_wayland_pointer_client_free (MetaWaylandPointerClient *pointer_client)
+meta_wayland_pointer_make_resources_inert (MetaWaylandPointerClient *pointer_client)
{
struct wl_resource *resource, *next;
@@ -141,10 +141,25 @@ meta_wayland_pointer_client_free (MetaWaylandPointerClient *pointer_client)
wl_list_init (wl_resource_get_link (resource));
wl_resource_set_user_data (resource, NULL);
}
+}
+static void
+meta_wayland_pointer_client_free (MetaWaylandPointerClient *pointer_client)
+{
+ meta_wayland_pointer_make_resources_inert (pointer_client);
g_free (pointer_client);
}
+static void
+make_resources_inert_foreach (gpointer key,
+ gpointer value,
+ gpointer data)
+{
+ MetaWaylandPointerClient *pointer_client = value;
+
+ meta_wayland_pointer_make_resources_inert (pointer_client);
+}
+
static gboolean
meta_wayland_pointer_client_is_empty (MetaWaylandPointerClient *pointer_client)
{
@@ -158,8 +173,6 @@ MetaWaylandPointerClient *
meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer,
struct wl_client *client)
{
- if (!pointer->pointer_clients)
- return NULL;
return g_hash_table_lookup (pointer->pointer_clients, client);
}
@@ -475,10 +488,6 @@ meta_wayland_pointer_enable (MetaWaylandPointer *pointer)
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
ClutterSeat *clutter_seat;
- pointer->pointer_clients =
- g_hash_table_new_full (NULL, NULL, NULL,
- (GDestroyNotify) meta_wayland_pointer_client_free);
-
pointer->cursor_surface = NULL;
clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
@@ -508,6 +517,10 @@ meta_wayland_pointer_disable (MetaWaylandPointer *pointer)
ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend);
+ g_hash_table_foreach (pointer->pointer_clients,
+ make_resources_inert_foreach,
+ NULL);
+
g_signal_handlers_disconnect_by_func (cursor_tracker,
(gpointer) meta_wayland_pointer_on_cursor_changed,
pointer);
@@ -531,7 +544,6 @@ meta_wayland_pointer_disable (MetaWaylandPointer *pointer)
meta_wayland_pointer_set_focus (pointer, NULL);
meta_wayland_pointer_set_current (pointer, NULL);
- g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref);
pointer->cursor_surface = NULL;
}
@@ -1356,11 +1368,28 @@ meta_wayland_pointer_init (MetaWaylandPointer *pointer)
pointer->default_grab.interface = &default_pointer_grab_interface;
pointer->default_grab.pointer = pointer;
pointer->grab = &pointer->default_grab;
+ pointer->pointer_clients =
+ g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) meta_wayland_pointer_client_free);
+}
+
+static void
+meta_wayland_pointer_finalize (GObject *object)
+{
+ MetaWaylandPointer *pointer = META_WAYLAND_POINTER (object);
+
+ g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref);
+
+ G_OBJECT_CLASS (meta_wayland_pointer_parent_class)->finalize (object);
}
static void
meta_wayland_pointer_class_init (MetaWaylandPointerClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = meta_wayland_pointer_finalize;
+
signals[FOCUS_SURFACE_CHANGED] = g_signal_new ("focus-surface-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c
index c6390dde7..efce6d6d6 100644
--- a/src/wayland/meta-wayland-seat.c
+++ b/src/wayland/meta-wayland-seat.c
@@ -46,8 +46,7 @@ seat_get_pointer (struct wl_client *client,
MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
MetaWaylandPointer *pointer = seat->pointer;
- if (meta_wayland_seat_has_pointer (seat))
- meta_wayland_pointer_create_new_resource (pointer, client, resource, id);
+ meta_wayland_pointer_create_new_resource (pointer, client, resource, id);
}
static void
@@ -58,8 +57,7 @@ seat_get_keyboard (struct wl_client *client,
MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
MetaWaylandKeyboard *keyboard = seat->keyboard;
- if (meta_wayland_seat_has_keyboard (seat))
- meta_wayland_keyboard_create_new_resource (keyboard, client, resource, id);
+ meta_wayland_keyboard_create_new_resource (keyboard, client, resource, id);
}
static void
@@ -70,8 +68,7 @@ seat_get_touch (struct wl_client *client,
MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
MetaWaylandTouch *touch = seat->touch;
- if (meta_wayland_seat_has_touch (seat))
- meta_wayland_touch_create_new_resource (touch, client, resource, id);
+ meta_wayland_touch_create_new_resource (touch, client, resource, id);
}
static void
diff --git a/src/wayland/meta-wayland-touch.c b/src/wayland/meta-wayland-touch.c
index 002ff16f7..15f0312eb 100644
--- a/src/wayland/meta-wayland-touch.c
+++ b/src/wayland/meta-wayland-touch.c
@@ -521,16 +521,8 @@ meta_wayland_touch_create_new_resource (MetaWaylandTouch *touch,
struct wl_resource *seat_resource,
uint32_t id)
{
- MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
struct wl_resource *cr;
- if (!meta_wayland_seat_has_touch (seat))
- {
- wl_resource_post_error (seat_resource, WL_DISPLAY_ERROR_INVALID_METHOD,
- "Cannot retrieve touch interface without touch capability");
- return;
- }
-
cr = wl_resource_create (client, &wl_touch_interface, wl_resource_get_version (seat_resource), id);
wl_resource_set_implementation (cr, &touch_interface, touch, unbind_resource);
wl_list_insert (&touch->resource_list, wl_resource_get_link (cr));
--
2.31.1