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