Bug#777142: patch - make gdk_event_apply_filters function safe against changes in filter list

Iain Lane iain at orangesquash.org.uk
Mon Feb 23 18:06:07 UTC 2015


Hi,

On Thu, Feb 05, 2015 at 05:58:04PM +0300, Vlad Orlov wrote:
> […]
> A serious flaw in gdk_event_apply_filters function is causing weird crashes
> in various software. For example, mate-display-properties crashes when
> the screen resolution is changed or a new monitor is plugged in [1]. Some
> other older bugs like [2] and [3] also might be caused by this one.

Cheers for the bug. Here's a debdiff. Mike (cced) said he would ask the
release team for a pre-upload unblock, so I'll hold off uploading until
we hear back. (Keep me in CC on that bug please).

Thanks,

-- 
Iain Lane                                  [ iain at orangesquash.org.uk ]
Debian Developer                                   [ laney at debian.org ]
Ubuntu Developer                                   [ laney at ubuntu.com ]
-------------- next part --------------
diff -Nru gtk+2.0-2.24.25/debian/changelog gtk+2.0-2.24.25/debian/changelog
--- gtk+2.0-2.24.25/debian/changelog	2014-10-10 18:33:16.000000000 +0100
+++ gtk+2.0-2.24.25/debian/changelog	2015-02-23 17:51:40.000000000 +0000
@@ -1,3 +1,12 @@
+gtk+2.0 (2.24.25-2) UNRELEASED; urgency=medium
+
+  * debian/patches/0001-Make-gdk_event_apply_filters-safe-against-changes-in.patch:
+    Cherry-pick patch from upstream stable branch to protect
+    gdk_event_apply_filters_safe from changes in the filter list (Closes:
+    #777142)
+
+ -- Iain Lane <laney at debian.org>  Mon, 23 Feb 2015 17:46:25 +0000
+
 gtk+2.0 (2.24.25-1) unstable; urgency=medium
 
   * Team upload
diff -Nru gtk+2.0-2.24.25/debian/control gtk+2.0-2.24.25/debian/control
--- gtk+2.0-2.24.25/debian/control	2014-10-10 18:34:04.000000000 +0100
+++ gtk+2.0-2.24.25/debian/control	2015-02-23 17:51:45.000000000 +0000
@@ -2,7 +2,7 @@
 Section: libs
 Priority: optional
 Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers at lists.alioth.debian.org>
-Uploaders: Emilio Pozuelo Monfort <pochu at debian.org>, Iain Lane <laney at debian.org>, Michael Biebl <biebl at debian.org>
+Uploaders: Iain Lane <laney at debian.org>, Michael Biebl <biebl at debian.org>
 Build-Depends: debhelper (>= 8.1.3),
                gettext,
                gtk-doc-tools (>= 1.11),
diff -Nru gtk+2.0-2.24.25/debian/patches/0001-Make-gdk_event_apply_filters-safe-against-changes-in.patch gtk+2.0-2.24.25/debian/patches/0001-Make-gdk_event_apply_filters-safe-against-changes-in.patch
--- gtk+2.0-2.24.25/debian/patches/0001-Make-gdk_event_apply_filters-safe-against-changes-in.patch	1970-01-01 01:00:00.000000000 +0100
+++ gtk+2.0-2.24.25/debian/patches/0001-Make-gdk_event_apply_filters-safe-against-changes-in.patch	2015-02-23 17:45:41.000000000 +0000
@@ -0,0 +1,351 @@
+From ee95f3d7259c0859ce41189b781b4339b4cd64aa Mon Sep 17 00:00:00 2001
+From: Matthias Clasen <mclasen at redhat.com>
+Date: Fri, 13 Feb 2015 13:12:39 -0500
+Subject: [PATCH] Make gdk_event_apply_filters safe against changes in filter
+ list
+
+An event filter may add or remove filters itself. This patch does
+two things to address this case. The first is to take a temporary
+reference to the filter while it is being used. The second is
+to wait until after the filter function is run before determining
+the next node in the list to process. This guards against
+changes to the next node. It also does not run functions
+that have been marked as removed. Though I'm not sure if this
+case can arise.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=635380
+
+Backport of 323df2b2800383832ed3c2e43626f2c6821c33ec to
+the gtk-2-24 branch by Wolfgang Ulbrich.
+
+http://bugs.debian.org/777142
+---
+ gdk/gdkinternals.h            |  6 +++++
+ gdk/gdkwindow.c               | 30 +++++++++++++++--------
+ gdk/quartz/gdkevents-quartz.c | 55 ++++++++++++++++++++++++++++++-------------
+ gdk/win32/gdkevents-win32.c   | 37 ++++++++++++++++++++++-------
+ gdk/x11/gdkevents-x11.c       | 37 ++++++++++++++++++++++-------
+ 5 files changed, 122 insertions(+), 43 deletions(-)
+
+diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
+index 0bd803f..97a1daa 100644
+--- a/gdk/gdkinternals.h
++++ b/gdk/gdkinternals.h
+@@ -59,9 +59,15 @@ struct _GdkColorInfo
+   guint ref_count;
+ };
+ 
++typedef enum {
++  GDK_EVENT_FILTER_REMOVED = 1 << 0
++} GdkEventFilterFlags;
++
+ struct _GdkEventFilter {
+   GdkFilterFunc function;
+   gpointer data;
++  GdkEventFilterFlags flags;
++  guint ref_count;
+ };
+ 
+ struct _GdkClientFilter {
+diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
+index 45fee34..29c878f 100644
+--- a/gdk/gdkwindow.c
++++ b/gdk/gdkwindow.c
+@@ -2545,13 +2545,18 @@ gdk_window_add_filter (GdkWindow     *window,
+     {
+       filter = (GdkEventFilter *)tmp_list->data;
+       if ((filter->function == function) && (filter->data == data))
+-	return;
++        {
++          filter->ref_count++;
++          return;
++        }
+       tmp_list = tmp_list->next;
+     }
+ 
+   filter = g_new (GdkEventFilter, 1);
+   filter->function = function;
+   filter->data = data;
++  filter->ref_count = 1;
++  filter->flags = 0;
+ 
+   if (private)
+     private->filters = g_list_append (private->filters, filter);
+@@ -2593,16 +2598,21 @@ gdk_window_remove_filter (GdkWindow     *window,
+       tmp_list = tmp_list->next;
+ 
+       if ((filter->function == function) && (filter->data == data))
+-	{
+-	  if (private)
+-	    private->filters = g_list_remove_link (private->filters, node);
+-	  else
+-	    _gdk_default_filters = g_list_remove_link (_gdk_default_filters, node);
+-	  g_list_free_1 (node);
+-	  g_free (filter);
++        {
++          filter->flags |= GDK_EVENT_FILTER_REMOVED;
++          filter->ref_count--;
++          if (filter->ref_count != 0)
++            return;
+ 
+-	  return;
+-	}
++          if (private)
++            private->filters = g_list_remove_link (private->filters, node);
++          else
++            _gdk_default_filters = g_list_remove_link (_gdk_default_filters, node);
++          g_list_free_1 (node);
++          g_free (filter);
++
++          return;
++        }
+     }
+ }
+ 
+diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c
+index 9e57edd..f199298 100644
+--- a/gdk/quartz/gdkevents-quartz.c
++++ b/gdk/quartz/gdkevents-quartz.c
+@@ -253,21 +253,42 @@ append_event (GdkEvent *event,
+ static gint
+ gdk_event_apply_filters (NSEvent *nsevent,
+ 			 GdkEvent *event,
+-			 GList *filters)
++			 GList **filters)
+ {
+   GList *tmp_list;
+   GdkFilterReturn result;
+   
+-  tmp_list = filters;
++  tmp_list = *filters;
+ 
+   while (tmp_list)
+     {
+       GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
+-      
+-      tmp_list = tmp_list->next;
++      GList *node;
++
++      if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
++        {
++          tmp_list = tmp_list->next;
++          continue;
++        }
++
++      filter->ref_count++;
+       result = filter->function (nsevent, event, filter->data);
+-      if (result !=  GDK_FILTER_CONTINUE)
+-	return result;
++
++      /* get the next node after running the function since the
++         function may add or remove a next node */
++      node = tmp_list;
++      tmp_list = tmp_list->next;
++
++      filter->ref_count--;
++      if (filter->ref_count == 0)
++        {
++          *filters = g_list_remove_link (*filters, node);
++          g_list_free_1 (node);
++          g_free (filter);
++        }
++
++      if (result != GDK_FILTER_CONTINUE)
++        return result;
+     }
+ 
+   return GDK_FILTER_CONTINUE;
+@@ -1319,7 +1340,7 @@ gdk_event_translate (GdkEvent *event,
+       /* Apply global filters */
+       GdkFilterReturn result;
+ 
+-      result = gdk_event_apply_filters (nsevent, event, _gdk_default_filters);
++      result = gdk_event_apply_filters (nsevent, event, &_gdk_default_filters);
+       if (result != GDK_FILTER_CONTINUE)
+         {
+           return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
+@@ -1390,19 +1411,19 @@ gdk_event_translate (GdkEvent *event,
+       GdkFilterReturn result;
+ 
+       if (filter_private->filters)
+-	{
+-	  g_object_ref (window);
++        {
++          g_object_ref (window);
+ 
+-	  result = gdk_event_apply_filters (nsevent, event, filter_private->filters);
++          result = gdk_event_apply_filters (nsevent, event, &filter_private->filters);
+ 
+-	  g_object_unref (window);
++          g_object_unref (window);
+ 
+-	  if (result != GDK_FILTER_CONTINUE)
+-	    {
+-	      return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
+-	      goto done;
+-	    }
+-	}
++          if (result != GDK_FILTER_CONTINUE)
++            {
++              return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
++              goto done;
++            }
++        }
+     }
+ 
+   /* If the app is not active leave the event to AppKit so the window gets
+diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
+index 8b345cb..c853e1e 100644
+--- a/gdk/win32/gdkevents-win32.c
++++ b/gdk/win32/gdkevents-win32.c
+@@ -1069,7 +1069,7 @@ fill_key_event_string (GdkEvent *event)
+ static GdkFilterReturn
+ apply_event_filters (GdkWindow  *window,
+ 		     MSG        *msg,
+-		     GList      *filters)
++		     GList      **filters)
+ {
+   GdkFilterReturn result = GDK_FILTER_CONTINUE;
+   GdkEvent *event;
+@@ -1087,15 +1087,36 @@ apply_event_filters (GdkWindow  *window,
+    */
+   node = _gdk_event_queue_append (_gdk_display, event);
+   
+-  tmp_list = filters;
++  tmp_list = *filters;
+   while (tmp_list)
+     {
+       GdkEventFilter *filter = (GdkEventFilter *) tmp_list->data;
+-      
+-      tmp_list = tmp_list->next;
++      GList *node;
++
++      if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
++        {
++          tmp_list = tmp_list->next;
++          continue;
++        }
++
++      filter->ref_count++;
+       result = filter->function (msg, event, filter->data);
+-      if (result !=  GDK_FILTER_CONTINUE)
+-	break;
++
++      /* get the next node after running the function since the
++         function may add or remove a next node */
++      node = tmp_list;
++      tmp_list = tmp_list->next;
++
++      filter->ref_count--;
++      if (filter->ref_count == 0)
++        {
++          *filters = g_list_remove_link (*filters, node);
++          g_list_free_1 (node);
++          g_free (filter);
++        }
++
++      if (result != GDK_FILTER_CONTINUE)
++        break;
+     }
+ 
+   if (result == GDK_FILTER_CONTINUE || result == GDK_FILTER_REMOVE)
+@@ -2075,7 +2096,7 @@ gdk_event_translate (MSG  *msg,
+     {
+       /* Apply global filters */
+ 
+-      GdkFilterReturn result = apply_event_filters (NULL, msg, _gdk_default_filters);
++      GdkFilterReturn result = apply_event_filters (NULL, msg, &_gdk_default_filters);
+       
+       /* If result is GDK_FILTER_CONTINUE, we continue as if nothing
+        * happened. If it is GDK_FILTER_REMOVE or GDK_FILTER_TRANSLATE,
+@@ -2121,7 +2142,7 @@ gdk_event_translate (MSG  *msg,
+     {
+       /* Apply per-window filters */
+ 
+-      GdkFilterReturn result = apply_event_filters (window, msg, ((GdkWindowObject *) window)->filters);
++      GdkFilterReturn result = apply_event_filters (window, msg, &((GdkWindowObject *) window)->filters);
+ 
+       if (result == GDK_FILTER_REMOVE || result == GDK_FILTER_TRANSLATE)
+ 	{
+diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c
+index b96e9f5..8de98c5 100644
+--- a/gdk/x11/gdkevents-x11.c
++++ b/gdk/x11/gdkevents-x11.c
+@@ -96,7 +96,7 @@ struct _GdkEventTypeX11
+ 
+ static gint	 gdk_event_apply_filters (XEvent   *xevent,
+ 					  GdkEvent *event,
+-					  GList    *filters);
++					  GList    **filters);
+ static gboolean	 gdk_event_translate	 (GdkDisplay *display,
+ 					  GdkEvent   *event, 
+ 					  XEvent     *xevent,
+@@ -341,21 +341,42 @@ gdk_event_get_graphics_expose (GdkWindow *window)
+ static gint
+ gdk_event_apply_filters (XEvent *xevent,
+ 			 GdkEvent *event,
+-			 GList *filters)
++			 GList **filters)
+ {
+   GList *tmp_list;
+   GdkFilterReturn result;
+   
+-  tmp_list = filters;
++  tmp_list = *filters;
+   
+   while (tmp_list)
+     {
+       GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
++      GList *node;
+       
+-      tmp_list = tmp_list->next;
++      if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
++        {
++          tmp_list = tmp_list->next;
++          continue;
++        }
++
++      filter->ref_count++;
+       result = filter->function (xevent, event, filter->data);
+-      if (result !=  GDK_FILTER_CONTINUE)
+-	return result;
++
++      /* get the next node after running the function since the
++         function may add or remove a next node */
++      node = tmp_list;
++      tmp_list = tmp_list->next;
++
++      filter->ref_count--;
++      if (filter->ref_count == 0)
++        {
++          *filters = g_list_remove_link (*filters, node);
++          g_list_free_1 (node);
++          g_free (filter);
++        }
++
++      if (result != GDK_FILTER_CONTINUE)
++        return result;
+     }
+   
+   return GDK_FILTER_CONTINUE;
+@@ -944,7 +965,7 @@ gdk_event_translate (GdkDisplay *display,
+       /* Apply global filters */
+       GdkFilterReturn result;
+       result = gdk_event_apply_filters (xevent, event,
+-                                        _gdk_default_filters);
++                                        &_gdk_default_filters);
+       
+       if (result != GDK_FILTER_CONTINUE)
+         {
+@@ -1050,7 +1071,7 @@ gdk_event_translate (GdkDisplay *display,
+ 	  g_object_ref (filter_window);
+ 	  
+ 	  result = gdk_event_apply_filters (xevent, event,
+-					    filter_private->filters);
++					    &filter_private->filters);
+ 	  
+ 	  g_object_unref (filter_window);
+       
+-- 
+2.1.4
+
diff -Nru gtk+2.0-2.24.25/debian/patches/series gtk+2.0-2.24.25/debian/patches/series
--- gtk+2.0-2.24.25/debian/patches/series	2014-08-10 13:47:58.000000000 +0100
+++ gtk+2.0-2.24.25/debian/patches/series	2015-02-23 17:45:00.000000000 +0000
@@ -1,3 +1,4 @@
+0001-Make-gdk_event_apply_filters-safe-against-changes-in.patch
 001_static-linking-dont-query-immodules.patch
 002_static-linking-dont-build-perf.patch
 003_gdk.pc_privates.patch
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.alioth.debian.org/pipermail/pkg-gnome-maintainers/attachments/20150223/e6736302/attachment.sig>


More information about the pkg-gnome-maintainers mailing list