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
Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/77>
This commit is contained in:
Olivier Fourdan 2017-11-28 10:54:08 +01:00 committed by Marge Bot
parent 73c6540028
commit 20bb8bf502
3 changed files with 40 additions and 22 deletions

View File

@ -109,7 +109,7 @@ meta_wayland_pointer_client_new (void)
} }
static 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; 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_list_init (wl_resource_get_link (resource));
wl_resource_set_user_data (resource, NULL); 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); 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 static gboolean
meta_wayland_pointer_client_is_empty (MetaWaylandPointerClient *pointer_client) meta_wayland_pointer_client_is_empty (MetaWaylandPointerClient *pointer_client)
{ {
@ -158,8 +173,6 @@ MetaWaylandPointerClient *
meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer, meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer,
struct wl_client *client) struct wl_client *client)
{ {
if (!pointer->pointer_clients)
return NULL;
return g_hash_table_lookup (pointer->pointer_clients, client); 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); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
ClutterSeat *clutter_seat; ClutterSeat *clutter_seat;
pointer->pointer_clients =
g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) meta_wayland_pointer_client_free);
pointer->cursor_surface = NULL; pointer->cursor_surface = NULL;
clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); 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 (); ClutterBackend *clutter_backend = clutter_get_default_backend ();
ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_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, g_signal_handlers_disconnect_by_func (cursor_tracker,
(gpointer) meta_wayland_pointer_on_cursor_changed, (gpointer) meta_wayland_pointer_on_cursor_changed,
pointer); pointer);
@ -531,7 +544,6 @@ meta_wayland_pointer_disable (MetaWaylandPointer *pointer)
meta_wayland_pointer_set_focus (pointer, NULL); meta_wayland_pointer_set_focus (pointer, NULL);
meta_wayland_pointer_set_current (pointer, NULL); meta_wayland_pointer_set_current (pointer, NULL);
g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref);
pointer->cursor_surface = NULL; 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.interface = &default_pointer_grab_interface;
pointer->default_grab.pointer = pointer; pointer->default_grab.pointer = pointer;
pointer->grab = &pointer->default_grab; 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 static void
meta_wayland_pointer_class_init (MetaWaylandPointerClass *klass) 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", signals[FOCUS_SURFACE_CHANGED] = g_signal_new ("focus-surface-changed",
G_TYPE_FROM_CLASS (klass), G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,

View File

@ -46,7 +46,6 @@ seat_get_pointer (struct wl_client *client,
MetaWaylandSeat *seat = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
MetaWaylandPointer *pointer = seat->pointer; 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);
} }
@ -58,7 +57,6 @@ seat_get_keyboard (struct wl_client *client,
MetaWaylandSeat *seat = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
MetaWaylandKeyboard *keyboard = seat->keyboard; 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);
} }
@ -70,7 +68,6 @@ seat_get_touch (struct wl_client *client,
MetaWaylandSeat *seat = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
MetaWaylandTouch *touch = seat->touch; 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);
} }

View File

@ -521,16 +521,8 @@ meta_wayland_touch_create_new_resource (MetaWaylandTouch *touch,
struct wl_resource *seat_resource, struct wl_resource *seat_resource,
uint32_t id) uint32_t id)
{ {
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
struct wl_resource *cr; 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); 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_resource_set_implementation (cr, &touch_interface, touch, unbind_resource);
wl_list_insert (&touch->resource_list, wl_resource_get_link (cr)); wl_list_insert (&touch->resource_list, wl_resource_get_link (cr));