094c4d
From 2bad3cb3bf8f0cc3f45057061f9a538ecf7742b6 Mon Sep 17 00:00:00 2001
094c4d
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
094c4d
Date: Thu, 14 Feb 2019 17:46:33 +0200
094c4d
Subject: [PATCH 1/5] Use atomic reference counting for GSource
094c4d
094c4d
If attached to a context already it would use a mutex instead but at
094c4d
least before that the reference counting is not thread-safe currently.
094c4d
---
094c4d
 glib/gmain.c | 50 +++++++++++++++-----------------------------------
094c4d
 1 file changed, 15 insertions(+), 35 deletions(-)
094c4d
094c4d
diff --git a/glib/gmain.c b/glib/gmain.c
094c4d
index 26e68823d..5b91c3117 100644
094c4d
--- a/glib/gmain.c
094c4d
+++ b/glib/gmain.c
094c4d
@@ -374,15 +374,6 @@ typedef struct _GSourceIter
094c4d
 #define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0)
094c4d
 #define SOURCE_BLOCKED(source) (((source)->flags & G_SOURCE_BLOCKED) != 0)
094c4d
 
094c4d
-#define SOURCE_UNREF(source, context)                       \
094c4d
-   G_STMT_START {                                           \
094c4d
-    if ((source)->ref_count > 1)                            \
094c4d
-      (source)->ref_count--;                                \
094c4d
-    else                                                    \
094c4d
-      g_source_unref_internal ((source), (context), TRUE);  \
094c4d
-   } G_STMT_END
094c4d
-
094c4d
-
094c4d
 /* Forward declarations */
094c4d
 
094c4d
 static void g_source_unref_internal             (GSource      *source,
094c4d
@@ -977,10 +968,10 @@ g_source_iter_next (GSourceIter *iter, GSource **source)
094c4d
    */
094c4d
 
094c4d
   if (iter->source && iter->may_modify)
094c4d
-    SOURCE_UNREF (iter->source, iter->context);
094c4d
+    g_source_unref_internal (iter->source, iter->context, TRUE);
094c4d
   iter->source = next_source;
094c4d
   if (iter->source && iter->may_modify)
094c4d
-    iter->source->ref_count++;
094c4d
+    g_source_ref (iter->source);
094c4d
 
094c4d
   *source = iter->source;
094c4d
   return *source != NULL;
094c4d
@@ -994,7 +985,7 @@ g_source_iter_clear (GSourceIter *iter)
094c4d
 {
094c4d
   if (iter->source && iter->may_modify)
094c4d
     {
094c4d
-      SOURCE_UNREF (iter->source, iter->context);
094c4d
+      g_source_unref_internal (iter->source, iter->context, TRUE);
094c4d
       iter->source = NULL;
094c4d
     }
094c4d
 }
094c4d
@@ -1135,7 +1126,7 @@ g_source_attach_unlocked (GSource      *source,
094c4d
 
094c4d
   source->context = context;
094c4d
   source->source_id = id;
094c4d
-  source->ref_count++;
094c4d
+  g_source_ref (source);
094c4d
 
094c4d
   g_hash_table_insert (context->sources, GUINT_TO_POINTER (id), source);
094c4d
 
094c4d
@@ -1675,7 +1666,7 @@ g_source_set_funcs (GSource     *source,
094c4d
 {
094c4d
   g_return_if_fail (source != NULL);
094c4d
   g_return_if_fail (source->context == NULL);
094c4d
-  g_return_if_fail (source->ref_count > 0);
094c4d
+  g_return_if_fail (g_atomic_int_get (&source->ref_count) > 0);
094c4d
   g_return_if_fail (funcs != NULL);
094c4d
 
094c4d
   source->source_funcs = funcs;
094c4d
@@ -2050,19 +2041,9 @@ g_source_set_name_by_id (guint           tag,
094c4d
 GSource *
094c4d
 g_source_ref (GSource *source)
094c4d
 {
094c4d
-  GMainContext *context;
094c4d
-  
094c4d
   g_return_val_if_fail (source != NULL, NULL);
094c4d
 
094c4d
-  context = source->context;
094c4d
-
094c4d
-  if (context)
094c4d
-    LOCK_CONTEXT (context);
094c4d
-
094c4d
-  source->ref_count++;
094c4d
-
094c4d
-  if (context)
094c4d
-    UNLOCK_CONTEXT (context);
094c4d
+  g_atomic_int_inc (&source->ref_count);
094c4d
 
094c4d
   return source;
094c4d
 }
094c4d
@@ -2078,12 +2059,11 @@ g_source_unref_internal (GSource      *source,
094c4d
   GSourceCallbackFuncs *old_cb_funcs = NULL;
094c4d
 
094c4d
   g_return_if_fail (source != NULL);
094c4d
-  
094c4d
+
094c4d
   if (!have_lock && context)
094c4d
     LOCK_CONTEXT (context);
094c4d
 
094c4d
-  source->ref_count--;
094c4d
-  if (source->ref_count == 0)
094c4d
+  if (g_atomic_int_dec_and_test (&source->ref_count))
094c4d
     {
094c4d
       TRACE (GLIB_SOURCE_BEFORE_FREE (source, context,
094c4d
                                       source->source_funcs->finalize));
094c4d
@@ -2107,20 +2087,20 @@ g_source_unref_internal (GSource      *source,
094c4d
 	{
094c4d
           /* Temporarily increase the ref count again so that GSource methods
094c4d
            * can be called from finalize(). */
094c4d
-          source->ref_count++;
094c4d
+          g_atomic_int_inc (&source->ref_count);
094c4d
 	  if (context)
094c4d
 	    UNLOCK_CONTEXT (context);
094c4d
 	  source->source_funcs->finalize (source);
094c4d
 	  if (context)
094c4d
 	    LOCK_CONTEXT (context);
094c4d
-          source->ref_count--;
094c4d
+          g_atomic_int_add (&source->ref_count, -1);
094c4d
 	}
094c4d
 
094c4d
       if (old_cb_funcs)
094c4d
         {
094c4d
           /* Temporarily increase the ref count again so that GSource methods
094c4d
            * can be called from callback_funcs.unref(). */
094c4d
-          source->ref_count++;
094c4d
+          g_atomic_int_inc (&source->ref_count);
094c4d
           if (context)
094c4d
             UNLOCK_CONTEXT (context);
094c4d
 
094c4d
@@ -2128,7 +2108,7 @@ g_source_unref_internal (GSource      *source,
094c4d
 
094c4d
           if (context)
094c4d
             LOCK_CONTEXT (context);
094c4d
-          source->ref_count--;
094c4d
+          g_atomic_int_add (&source->ref_count, -1);
094c4d
         }
094c4d
 
094c4d
       g_free (source->name);
094c4d
@@ -3201,7 +3181,7 @@ g_main_dispatch (GMainContext *context)
094c4d
 	    }
094c4d
 	}
094c4d
       
094c4d
-      SOURCE_UNREF (source, context);
094c4d
+      g_source_unref_internal (source, context, TRUE);
094c4d
     }
094c4d
 
094c4d
   g_ptr_array_set_size (context->pending_dispatches, 0);
094c4d
@@ -3440,7 +3420,7 @@ g_main_context_prepare (GMainContext *context,
094c4d
   for (i = 0; i < context->pending_dispatches->len; i++)
094c4d
     {
094c4d
       if (context->pending_dispatches->pdata[i])
094c4d
-	SOURCE_UNREF ((GSource *)context->pending_dispatches->pdata[i], context);
094c4d
+        g_source_unref_internal ((GSource *)context->pending_dispatches->pdata[i], context, TRUE);
094c4d
     }
094c4d
   g_ptr_array_set_size (context->pending_dispatches, 0);
094c4d
   
094c4d
@@ -3788,7 +3768,7 @@ g_main_context_check (GMainContext *context,
094c4d
 
094c4d
       if (source->flags & G_SOURCE_READY)
094c4d
 	{
094c4d
-	  source->ref_count++;
094c4d
+          g_source_ref (source);
094c4d
 	  g_ptr_array_add (context->pending_dispatches, source);
094c4d
 
094c4d
 	  n_ready++;
094c4d
-- 
094c4d
2.31.1
094c4d
094c4d
From 323d0c7658a9a44efc327840c0667044a4b98f89 Mon Sep 17 00:00:00 2001
094c4d
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
094c4d
Date: Mon, 3 Feb 2020 15:38:28 +0200
094c4d
Subject: [PATCH 2/5] GMainContext - Fix GSource iterator if iteration can
094c4d
 modify the list
094c4d
094c4d
We first have to ref the next source and then unref the previous one.
094c4d
This might be the last reference to the previous source, and freeing the
094c4d
previous source might unref and free the next one which would then leave
094c4d
use with a dangling pointer here.
094c4d
094c4d
Fixes https://gitlab.gnome.org/GNOME/glib/issues/2031
094c4d
---
094c4d
 glib/gmain.c | 8 ++++++--
094c4d
 1 file changed, 6 insertions(+), 2 deletions(-)
094c4d
094c4d
diff --git a/glib/gmain.c b/glib/gmain.c
094c4d
index 5b91c3117..a3ea1d36c 100644
094c4d
--- a/glib/gmain.c
094c4d
+++ b/glib/gmain.c
094c4d
@@ -965,13 +965,17 @@ g_source_iter_next (GSourceIter *iter, GSource **source)
094c4d
    * GSourceList to be removed from source_lists (if iter->source is
094c4d
    * the only source in its list, and it is destroyed), so we have to
094c4d
    * keep it reffed until after we advance iter->current_list, above.
094c4d
+   *
094c4d
+   * Also we first have to ref the next source before unreffing the
094c4d
+   * previous one as unreffing the previous source can potentially
094c4d
+   * free the next one.
094c4d
    */
094c4d
+  if (next_source && iter->may_modify)
094c4d
+    g_source_ref (next_source);
094c4d
 
094c4d
   if (iter->source && iter->may_modify)
094c4d
     g_source_unref_internal (iter->source, iter->context, TRUE);
094c4d
   iter->source = next_source;
094c4d
-  if (iter->source && iter->may_modify)
094c4d
-    g_source_ref (iter->source);
094c4d
 
094c4d
   *source = iter->source;
094c4d
   return *source != NULL;
094c4d
-- 
094c4d
2.31.1
094c4d
094c4d
From fc051ec83d8894dd754bf364562ba9be9ff999fc Mon Sep 17 00:00:00 2001
094c4d
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
094c4d
Date: Mon, 3 Feb 2020 15:35:51 +0200
094c4d
Subject: [PATCH 3/5] GMainContext - Fix memory leaks and memory corruption
094c4d
 when freeing sources while freeing a context
094c4d
094c4d
Instead of destroying sources directly while freeing the context, and
094c4d
potentially freeing them if this was the last reference to them, collect
094c4d
new references of all sources in a separate list before and at the same
094c4d
time invalidate their context so that they can't access it anymore. Only
094c4d
once all sources have their context invalidated, destroy them while
094c4d
still keeping a reference to them. Once all sources are destroyed we get
094c4d
rid of the additional references and free them if nothing else keeps a
094c4d
reference to them anymore.
094c4d
094c4d
This fixes a regression introduced by 26056558be in 2012.
094c4d
094c4d
The previous code that invalidated the context of each source and then
094c4d
destroyed it before going to the next source without keeping an
094c4d
additional reference caused memory leaks or memory corruption depending
094c4d
on the order of the sources in the sources lists.
094c4d
094c4d
If a source was destroyed it might happen that this was the last
094c4d
reference to this source, and it would then be freed. This would cause
094c4d
the finalize function to be called, which might destroy and unref
094c4d
another source and potentially free it. This other source would then
094c4d
either
094c4d
- go through the normal free logic and change the intern linked list
094c4d
  between the sources, while other sources that are unreffed as part of
094c4d
  the main context freeing would not. As such the list would be in an
094c4d
  inconsistent state and we might dereference freed memory.
094c4d
- go through the normal destroy and free logic but because the context
094c4d
  pointer was already invalidated it would simply mark the source as
094c4d
  destroyed without actually removing it from the context. This would
094c4d
  then cause a memory leak because the reference owned by the context is
094c4d
  not freed.
094c4d
094c4d
Fixes https://github.com/gtk-rs/glib/issues/583 while still keeping
094c4d
https://bugzilla.gnome.org/show_bug.cgi?id=661767 fixes.
094c4d
---
094c4d
 glib/gmain.c | 35 ++++++++++++++++++++++++++++++++++-
094c4d
 1 file changed, 34 insertions(+), 1 deletion(-)
094c4d
094c4d
diff --git a/glib/gmain.c b/glib/gmain.c
094c4d
index a3ea1d36c..1c249ad02 100644
094c4d
--- a/glib/gmain.c
094c4d
+++ b/glib/gmain.c
094c4d
@@ -534,6 +534,7 @@ g_main_context_unref (GMainContext *context)
094c4d
   GSourceIter iter;
094c4d
   GSource *source;
094c4d
   GList *sl_iter;
094c4d
+  GSList *s_iter, *remaining_sources = NULL;
094c4d
   GSourceList *list;
094c4d
   guint i;
094c4d
 
094c4d
@@ -553,10 +554,30 @@ g_main_context_unref (GMainContext *context)
094c4d
 
094c4d
   /* g_source_iter_next() assumes the context is locked. */
094c4d
   LOCK_CONTEXT (context);
094c4d
-  g_source_iter_init (&iter, context, TRUE);
094c4d
+
094c4d
+  /* First collect all remaining sources from the sources lists and store a
094c4d
+   * new reference in a separate list. Also set the context of the sources
094c4d
+   * to NULL so that they can't access a partially destroyed context anymore.
094c4d
+   *
094c4d
+   * We have to do this first so that we have a strong reference to all
094c4d
+   * sources and destroying them below does not also free them, and so that
094c4d
+   * none of the sources can access the context from their finalize/dispose
094c4d
+   * functions. */
094c4d
+  g_source_iter_init (&iter, context, FALSE);
094c4d
   while (g_source_iter_next (&iter, &source))
094c4d
     {
094c4d
       source->context = NULL;
094c4d
+      remaining_sources = g_slist_prepend (remaining_sources, g_source_ref (source));
094c4d
+    }
094c4d
+  g_source_iter_clear (&iter);
094c4d
+
094c4d
+  /* Next destroy all sources. As we still hold a reference to all of them,
094c4d
+   * this won't cause any of them to be freed yet and especially prevents any
094c4d
+   * source that unrefs another source from its finalize function to be freed.
094c4d
+   */
094c4d
+  for (s_iter = remaining_sources; s_iter; s_iter = s_iter->next)
094c4d
+    {
094c4d
+      source = s_iter->data;
094c4d
       g_source_destroy_internal (source, context, TRUE);
094c4d
     }
094c4d
   UNLOCK_CONTEXT (context);
094c4d
@@ -581,6 +602,18 @@ g_main_context_unref (GMainContext *context)
094c4d
   g_cond_clear (&context->cond);
094c4d
 
094c4d
   g_free (context);
094c4d
+
094c4d
+  /* And now finally get rid of our references to the sources. This will cause
094c4d
+   * them to be freed unless something else still has a reference to them. Due
094c4d
+   * to setting the context pointers in the sources to NULL above, this won't
094c4d
+   * ever access the context or the internal linked list inside the GSource.
094c4d
+   * We already removed the sources completely from the context above. */
094c4d
+  for (s_iter = remaining_sources; s_iter; s_iter = s_iter->next)
094c4d
+    {
094c4d
+      source = s_iter->data;
094c4d
+      g_source_unref_internal (source, NULL, FALSE);
094c4d
+    }
094c4d
+  g_slist_free (remaining_sources);
094c4d
 }
094c4d
 
094c4d
 /* Helper function used by mainloop/overflow test.
094c4d
-- 
094c4d
2.31.1
094c4d
094c4d
From 1d16e92028f235ed9cd786070832d5bd71017661 Mon Sep 17 00:00:00 2001
094c4d
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian@centricular.com>
094c4d
Date: Tue, 11 Feb 2020 09:34:38 +0200
094c4d
Subject: [PATCH 4/5] GMainContext - Move mutex unlocking in destructor right
094c4d
 before freeing the mutex
094c4d
094c4d
This does not have any behaviour changes but is cleaner. The mutex is
094c4d
only unlocked now after all operations on the context are done and right
094c4d
before freeing the mutex and the context itself.
094c4d
---
094c4d
 glib/gmain.c | 2 +-
094c4d
 1 file changed, 1 insertion(+), 1 deletion(-)
094c4d
094c4d
diff --git a/glib/gmain.c b/glib/gmain.c
094c4d
index 1c249ad02..44e6ed0c3 100644
094c4d
--- a/glib/gmain.c
094c4d
+++ b/glib/gmain.c
094c4d
@@ -580,7 +580,6 @@ g_main_context_unref (GMainContext *context)
094c4d
       source = s_iter->data;
094c4d
       g_source_destroy_internal (source, context, TRUE);
094c4d
     }
094c4d
-  UNLOCK_CONTEXT (context);
094c4d
 
094c4d
   for (sl_iter = context->source_lists; sl_iter; sl_iter = sl_iter->next)
094c4d
     {
094c4d
@@ -591,6 +590,7 @@ g_main_context_unref (GMainContext *context)
094c4d
 
094c4d
   g_hash_table_destroy (context->sources);
094c4d
 
094c4d
+  UNLOCK_CONTEXT (context);
094c4d
   g_mutex_clear (&context->mutex);
094c4d
 
094c4d
   g_ptr_array_free (context->pending_dispatches, TRUE);
094c4d
-- 
094c4d
2.31.1
094c4d
094c4d
From 02ad7294ad5895178df73a6cd8546c6e67097493 Mon Sep 17 00:00:00 2001
094c4d
From: Benjamin Berg <bberg@redhat.com>
094c4d
Date: Tue, 13 Oct 2020 15:09:43 +0200
094c4d
Subject: [PATCH 5/5] gmain: Fix possible locking issue in source unref
094c4d
094c4d
When unref'ing child sources, the lock is already held. But instead of
094c4d
passing TRUE to g_source_unref_internal it currently passes whether the
094c4d
lock was already held outside of the current invocation. Just pass TRUE
094c4d
to fix this possible issue.
094c4d
---
094c4d
 glib/gmain.c | 2 +-
094c4d
 1 file changed, 1 insertion(+), 1 deletion(-)
094c4d
094c4d
diff --git a/glib/gmain.c b/glib/gmain.c
094c4d
index 44e6ed0c3..95992253d 100644
094c4d
--- a/glib/gmain.c
094c4d
+++ b/glib/gmain.c
094c4d
@@ -2164,7 +2164,7 @@ g_source_unref_internal (GSource      *source,
094c4d
             g_slist_remove (source->priv->child_sources, child_source);
094c4d
           child_source->priv->parent_source = NULL;
094c4d
 
094c4d
-          g_source_unref_internal (child_source, context, have_lock);
094c4d
+          g_source_unref_internal (child_source, context, TRUE);
094c4d
         }
094c4d
 
094c4d
       g_slice_free (GSourcePrivate, source->priv);
094c4d
-- 
094c4d
2.31.1