Submitted by:            Xi Ruoyao <xry111@xry111.site>
Date:                    2025-11-19
Initial Package Version: 8.2.10
Upstream Status:         Not applicable: the correct fix is
                         https://github.com/bdwgc/bdwgc/pull/790 but the
                         8.2.10 release is too different from the
                         development branch to get it backported.  The
                         upstream suggests to use this as a temporary (until
                         the next minor release) workaround.  Arch and AOSC
                         OS have already shipped this workaround.
Origin:                  https://github.com/AOSC-Dev/aosc-os-abbs/tree/08b4b12f2582/runtime-common/gc/autobuild/patches
Description:             Revert two commits triggering a crash of
                         applications using GC and libraries forking
                         subprocesses (for example glycin) at the same time.
                         Inkscape is such an application in the book.

From 57b81b139e25b25159addbf14dcb78e7df7be781 Mon Sep 17 00:00:00 2001
From: Mingcong Bai <jeffbai@aosc.io>
Date: Sun, 16 Nov 2025 12:51:43 +0800
Subject: [PATCH 2/3] AOSCOS: Revert "Fix pthread id stored in key
 thread_specific_data of child process"

This reverts commit 2cd0f5e56718a053341d1b5e7df7a3cab9a5ceaa.

Link: https://github.com/bdwgc/bdwgc/issues/783#issuecomment-3433334711
Signed-off-by: Mingcong Bai <jeffbai@aosc.io>
---
 include/private/gc_priv.h  | 11 -----------
 include/private/specific.h |  7 -------
 pthread_support.c          | 31 +++++++++++++++----------------
 specific.c                 | 27 ---------------------------
 4 files changed, 15 insertions(+), 61 deletions(-)

diff --git a/include/private/gc_priv.h b/include/private/gc_priv.h
index 5e14fd72..90c7f997 100644
--- a/include/private/gc_priv.h
+++ b/include/private/gc_priv.h
@@ -832,10 +832,6 @@ EXTERN_C_END
 
 #include <setjmp.h>
 
-#if defined(CAN_HANDLE_FORK) && defined(GC_PTHREADS)
-#  include <pthread.h> /* for pthread_t */
-#endif
-
 #if __STDC_VERSION__ >= 201112L
 # include <assert.h> /* for static_assert */
 #endif
@@ -1556,13 +1552,6 @@ struct _GC_arrays {
 # endif
   size_t _ed_size;      /* Current size of above arrays.        */
   size_t _avail_descr;  /* Next available slot.                 */
-
-# if defined(CAN_HANDLE_FORK) && defined(GC_PTHREADS)
-    /* Value of pthread_self() of the thread which called fork().   */
-#   define GC_parent_pthread_self GC_arrays._parent_pthread_self
-    pthread_t _parent_pthread_self;
-# endif
-
   typed_ext_descr_t *_ext_descriptors;  /* Points to array of extended  */
                                         /* descriptors.                 */
   GC_mark_proc _mark_procs[MAX_MARK_PROCS];
diff --git a/include/private/specific.h b/include/private/specific.h
index 029fc7f3..3ed75647 100644
--- a/include/private/specific.h
+++ b/include/private/specific.h
@@ -109,13 +109,6 @@ GC_INNER int GC_setspecific(tsd * key, void * value);
                         GC_remove_specific_after_fork(key, pthread_self())
 GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t);
 
-#ifdef CAN_HANDLE_FORK
-  /* Update thread-specific data for the survived thread of the child     */
-  /* process.  Should be called once after removing thread-specific data  */
-  /* for other threads.                                                   */
-  GC_INNER void GC_update_specific_after_fork(tsd *key);
-#endif
-
 /* An internal version of getspecific that assumes a cache miss.        */
 GC_INNER void * GC_slow_getspecific(tsd * key, word qtid,
                                     tse * volatile * cache_entry);
diff --git a/pthread_support.c b/pthread_support.c
index 937846c7..3a4b22b1 100644
--- a/pthread_support.c
+++ b/pthread_support.c
@@ -829,6 +829,9 @@ GC_API void GC_CALL GC_register_altstack(void *stack, GC_word stack_size,
     GC_threads[hv] = me;
   }
 
+/* Value of pthread_self() of the thread which called fork(). */
+STATIC pthread_t GC_parent_pthread_self;
+
 /* Remove all entries from the GC_threads table, except the one for */
 /* the current thread.  Also update thread identifiers stored in    */
 /* the table for the current thread.  We need to do this in the     */
@@ -892,22 +895,18 @@ STATIC void GC_remove_all_threads_but_me(void)
     /* Put "me" back to GC_threads.     */
     store_to_threads_table(THREAD_TABLE_INDEX(me -> id), me);
 
-#   ifdef THREAD_LOCAL_ALLOC
-#     ifdef USE_CUSTOM_SPECIFIC
-        GC_update_specific_after_fork(GC_thread_key);
-#     else
-        {
-          int res;
-
-          /* Some TLS implementations might be not fork-friendly, so    */
-          /* we re-assign thread-local pointer to 'tlfs' for safety     */
-          /* instead of the assertion check (again, it is OK to call    */
-          /* GC_destroy_thread_local and GC_free_inner before).         */
-          res = GC_setspecific(GC_thread_key, &me->tlfs);
-          if (COVERT_DATAFLOW(res) != 0)
-            ABORT("GC_setspecific failed (in child)");
-        }
-#     endif
+#   if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC)
+    {
+      int res;
+
+      /* Some TLS implementations might be not fork-friendly, so    */
+      /* we re-assign thread-local pointer to 'tlfs' for safety     */
+      /* instead of the assertion check (again, it is OK to call    */
+      /* GC_destroy_thread_local and GC_free_inner before).         */
+      res = GC_setspecific(GC_thread_key, &me->tlfs);
+      if (COVERT_DATAFLOW(res) != 0)
+        ABORT("GC_setspecific failed (in child)");
+    }
 #   endif
 }
 #endif /* CAN_HANDLE_FORK */
diff --git a/specific.c b/specific.c
index 1293d40b..09387ac3 100644
--- a/specific.c
+++ b/specific.c
@@ -133,33 +133,6 @@ GC_INNER void GC_remove_specific_after_fork(tsd * key, pthread_t t)
       ABORT("pthread_mutex_unlock failed (remove_specific after fork)");
 }
 
-#ifdef CAN_HANDLE_FORK
-  GC_INNER void
-  GC_update_specific_after_fork(tsd *key)
-  {
-    unsigned hash_val = HASH(GC_parent_pthread_self);
-    tse *entry;
-
-    GC_ASSERT(I_HOLD_LOCK());
-#   ifdef LINT2
-      pthread_mutex_lock(&key->lock);
-#   endif
-    entry = key->hash[hash_val].p;
-    if (EXPECT(entry != NULL, TRUE)) {
-      GC_ASSERT(THREAD_EQUAL(entry->thread, GC_parent_pthread_self));
-      GC_ASSERT(NULL == entry->next);
-      /* Remove the entry from the table. */
-      key->hash[hash_val].p = NULL;
-      entry->thread = pthread_self();
-      /* Then put the entry back to the table (based on new hash value). */
-      key->hash[HASH(entry->thread)].p = entry;
-    }
-#   ifdef LINT2
-      (void)pthread_mutex_unlock(&key->lock);
-#   endif
-  }
-#endif
-
 /* Note that even the slow path doesn't lock.   */
 GC_INNER void * GC_slow_getspecific(tsd * key, word qtid,
                                     tse * volatile * cache_ptr)
-- 
2.51.1

From f2ec374b22ef3e1631fc87d5dd9fb6ef10acb5e4 Mon Sep 17 00:00:00 2001
From: Mingcong Bai <jeffbai@aosc.io>
Date: Sun, 16 Nov 2025 12:51:53 +0800
Subject: [PATCH 3/3] AOSCOS: Revert "Fix pthread id stored in GC_threads of
 child process"

This reverts commit 74fc05d128c4ad9860740d73b4c7fd43ea18e74a.

Link: https://github.com/bdwgc/bdwgc/issues/783#issuecomment-3433334711
Signed-off-by: Mingcong Bai <jeffbai@aosc.io>
---
 pthread_support.c | 84 +++++++++++++++++------------------------------
 1 file changed, 30 insertions(+), 54 deletions(-)

diff --git a/pthread_support.c b/pthread_support.c
index 3a4b22b1..3d85bc59 100644
--- a/pthread_support.c
+++ b/pthread_support.c
@@ -829,28 +829,47 @@ GC_API void GC_CALL GC_register_altstack(void *stack, GC_word stack_size,
     GC_threads[hv] = me;
   }
 
-/* Value of pthread_self() of the thread which called fork(). */
-STATIC pthread_t GC_parent_pthread_self;
-
-/* Remove all entries from the GC_threads table, except the one for */
-/* the current thread.  Also update thread identifiers stored in    */
-/* the table for the current thread.  We need to do this in the     */
-/* child process after a fork(), since only the current thread      */
-/* survives in the child.                                           */
+/* Remove all entries from the GC_threads table, except the     */
+/* one for the current thread.  We need to do this in the child */
+/* process after a fork(), since only the current thread        */
+/* survives in the child.                                       */
 STATIC void GC_remove_all_threads_but_me(void)
 {
+    pthread_t self = pthread_self();
     int hv;
-    GC_thread me = NULL;
 
     for (hv = 0; hv < THREAD_TABLE_SZ; ++hv) {
       GC_thread p, next;
+      GC_thread me = NULL;
 
       for (p = GC_threads[hv]; 0 != p; p = next) {
         next = p -> next;
-        if (THREAD_EQUAL(p -> id, GC_parent_pthread_self)
+        if (THREAD_EQUAL(p -> id, self)
             && me == NULL) { /* ignore dead threads with the same id */
           me = p;
           p -> next = 0;
+#         ifdef GC_DARWIN_THREADS
+            /* Update thread Id after fork (it is OK to call    */
+            /* GC_destroy_thread_local and GC_free_inner        */
+            /* before update).                                  */
+            me -> stop_info.mach_thread = mach_thread_self();
+#         endif
+#         ifdef USE_TKILL_ON_ANDROID
+            me -> kernel_id = gettid();
+#         endif
+#         if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC)
+          {
+            int res;
+
+            /* Some TLS implementations might be not fork-friendly, so  */
+            /* we re-assign thread-local pointer to 'tlfs' for safety   */
+            /* instead of the assertion check (again, it is OK to call  */
+            /* GC_destroy_thread_local and GC_free_inner before).       */
+            res = GC_setspecific(GC_thread_key, &me->tlfs);
+            if (COVERT_DATAFLOW(res) != 0)
+              ABORT("GC_setspecific failed (in child)");
+          }
+#         endif
         } else {
 #         ifdef THREAD_LOCAL_ALLOC
             if (!(p -> flags & FINISHED)) {
@@ -870,44 +889,8 @@ STATIC void GC_remove_all_threads_but_me(void)
 #         endif
         }
       }
-      store_to_threads_table(hv, NULL);
-    }
-
-#   if defined(CPPCHECK) || defined(LINT2)
-      if (NULL == me)
-        ABORT("Current thread is not found after fork");
-#   else
-      GC_ASSERT(me != NULL);
-#   endif
-    /* Update pthread's id as it is not guaranteed to be the same   */
-    /* between this (child) process and the parent one.             */
-    me -> id = pthread_self();
-#   ifdef GC_DARWIN_THREADS
-      /* Update thread Id after fork (it is OK to call  */
-      /* GC_destroy_thread_local and GC_free_inner      */
-      /* before update).                                */
-      me -> stop_info.mach_thread = mach_thread_self();
-#   endif
-#   ifdef USE_TKILL_ON_ANDROID
-      me -> kernel_id = gettid();
-#   endif
-
-    /* Put "me" back to GC_threads.     */
-    store_to_threads_table(THREAD_TABLE_INDEX(me -> id), me);
-
-#   if defined(THREAD_LOCAL_ALLOC) && !defined(USE_CUSTOM_SPECIFIC)
-    {
-      int res;
-
-      /* Some TLS implementations might be not fork-friendly, so    */
-      /* we re-assign thread-local pointer to 'tlfs' for safety     */
-      /* instead of the assertion check (again, it is OK to call    */
-      /* GC_destroy_thread_local and GC_free_inner before).         */
-      res = GC_setspecific(GC_thread_key, &me->tlfs);
-      if (COVERT_DATAFLOW(res) != 0)
-        ABORT("GC_setspecific failed (in child)");
+      store_to_threads_table(hv, me);
     }
-#   endif
 }
 #endif /* CAN_HANDLE_FORK */
 
@@ -1214,7 +1197,6 @@ static void fork_prepare_proc(void)
     /* the (one remaining thread in) the child.                         */
       LOCK();
       DISABLE_CANCEL(fork_cancel_state);
-      GC_parent_pthread_self = pthread_self();
                 /* Following waits may include cancellation points. */
 #     if defined(PARALLEL_MARK)
         if (GC_parallel)
@@ -1255,9 +1237,6 @@ static void fork_parent_proc(void)
       }
 #   endif
     RESTORE_CANCEL(fork_cancel_state);
-#   ifdef GC_ASSERTIONS
-      BZERO(&GC_parent_pthread_self, sizeof(pthread_t));
-#   endif
     UNLOCK();
 }
 
@@ -1305,9 +1284,6 @@ static void fork_child_proc(void)
     /* Clean up the thread table, so that just our thread is left.      */
     GC_remove_all_threads_but_me();
     RESTORE_CANCEL(fork_cancel_state);
-#   ifdef GC_ASSERTIONS
-      BZERO(&GC_parent_pthread_self, sizeof(pthread_t));
-#   endif
     UNLOCK();
     /* Even though after a fork the child only inherits the single      */
     /* thread that called the fork(), if another thread in the parent   */
-- 
2.51.1

