wayland: protect against unknown mimetype requests
authorCarlos Garnacho <carlosg@gnome.org>
Wed, 24 Jun 2015 20:10:55 +0000 (22:10 +0200)
committerCarlos Garnacho <carlosg@gnome.org>
Wed, 24 Jun 2015 21:52:50 +0000 (23:52 +0200)
This oddly can be reproduced with weston+weston-dnd, when dragging
anything from GTK+ into weston-dnd, it will insist on picking its
custom application/x-wayland-dnd-flower mimetype, and this request
forwarded by the compositor, even if GTK+ didn't announce it on
its wl_data_source mimetype list. (What should probably happen here
is that the request is silenced, and/or weston-dnd picks (null))

This should be harmless, we are leaking though the fd in that case,
because the emission of GdkEventSelection on an unhandled mimetype
results in NOP. In order to avoid this, we should check whether the
mimetype is supported at all on the backend code and possibly close
the fd, this involves storing these in the first place.

https://bugzilla.gnome.org/show_bug.cgi?id=751414

gdk/wayland/gdkselection-wayland.c

index ac5d38e22d6e12a8a9196ef52681f8850d09df70..d0e7e7d73f6cce2d944c002e0a41cb7e6936a1b3 100644 (file)
@@ -89,6 +89,7 @@ struct _GdkWaylandSelection
 
   /* Source-side data */
   StoredSelection stored_selection;
+  GArray *source_targets;
 
   struct wl_data_source *clipboard_source;
   GdkWindow *clipboard_owner;
@@ -273,6 +274,7 @@ gdk_wayland_selection_new (void)
       g_hash_table_new_full (NULL, NULL, NULL,
                              (GDestroyNotify) selection_buffer_cancel_and_unref);
   selection->stored_selection.fd = -1;
+  selection->source_targets = g_array_new (FALSE, FALSE, sizeof (GdkAtom));
   return selection;
 }
 
@@ -280,6 +282,7 @@ void
 gdk_wayland_selection_free (GdkWaylandSelection *selection)
 {
   g_hash_table_destroy (selection->selection_buffers);
+  g_array_unref (selection->source_targets);
 
   if (selection->targets)
     g_list_free (selection->targets);
@@ -548,6 +551,27 @@ gdk_wayland_selection_lookup_requestor_buffer (GdkWindow *requestor)
   return NULL;
 }
 
+static gboolean
+gdk_wayland_selection_source_handles_target (GdkWaylandSelection *wayland_selection,
+                                             GdkAtom              target)
+{
+  GdkAtom atom;
+  guint i;
+
+  if (target == GDK_NONE)
+    return FALSE;
+
+  for (i = 0; i < wayland_selection->source_targets->len; i++)
+    {
+      atom = g_array_index (wayland_selection->source_targets, GdkAtom, i);
+
+      if (atom == target)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
 static gboolean
 gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection,
                                       GdkWindow           *window,
@@ -578,11 +602,17 @@ gdk_wayland_selection_request_target (GdkWaylandSelection *wayland_selection,
 
   wayland_selection->source_requested_target = target;
 
-  if (window && target != GDK_NONE)
+  if (window &&
+      gdk_wayland_selection_source_handles_target (wayland_selection, target))
     {
       gdk_wayland_selection_emit_request (window, selection, target);
       return TRUE;
     }
+  else
+    {
+      close (fd);
+      wayland_selection->stored_selection.fd = -1;
+    }
 
   return FALSE;
 }
@@ -1027,6 +1057,8 @@ gdk_wayland_selection_add_targets (GdkWindow *window,
                                    guint      ntargets,
                                    GdkAtom   *targets)
 {
+  GdkDisplay *display = gdk_window_get_display (window);
+  GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
   struct wl_data_source *data_source;
   guint i;
 
@@ -1037,6 +1069,8 @@ gdk_wayland_selection_add_targets (GdkWindow *window,
   if (!data_source)
     return;
 
+  g_array_append_vals (wayland_selection->source_targets, targets, ntargets);
+
   for (i = 0; i < ntargets; i++)
     wl_data_source_offer (data_source, gdk_atom_name (targets[i]));
 
@@ -1057,5 +1091,8 @@ void
 gdk_wayland_selection_clear_targets (GdkDisplay *display,
                                      GdkAtom     selection)
 {
+  GdkWaylandSelection *wayland_selection = gdk_wayland_display_get_selection (display);
+
+  g_array_set_size (wayland_selection->source_targets, 0);
   gdk_wayland_selection_unset_data_source (display, selection);
 }