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