commit:     202d134fb22f2d21ff420b08215e921d42898b93
Author:     Sam James <sam <AT> gentoo <DOT> org>
AuthorDate: Thu Dec 18 04:49:16 2025 +0000
Commit:     Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Thu Dec 18 04:55:20 2025 +0000
URL:        https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=202d134f

dev-libs/boehm-gc: backport crash fix(es) to 8.2.10

Signed-off-by: Sam James <sam <AT> gentoo.org>

 dev-libs/boehm-gc/boehm-gc-8.2.10-r1.ebuild        |  67 ++++++
 .../boehm-gc/files/boehm-gc-8.2.10-crash.patch     | 258 +++++++++++++++++++++
 2 files changed, 325 insertions(+)

diff --git a/dev-libs/boehm-gc/boehm-gc-8.2.10-r1.ebuild 
b/dev-libs/boehm-gc/boehm-gc-8.2.10-r1.ebuild
new file mode 100644
index 000000000000..d3b1a772171c
--- /dev/null
+++ b/dev-libs/boehm-gc/boehm-gc-8.2.10-r1.ebuild
@@ -0,0 +1,67 @@
+# Copyright 1999-2025 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+inherit dot-a multilib-minimal libtool
+
+MY_P="gc-${PV}"
+
+DESCRIPTION="The Boehm-Demers-Weiser conservative garbage collector"
+HOMEPAGE="https://www.hboehm.info/gc/ https://github.com/bdwgc/bdwgc/";
+SRC_URI="https://github.com/bdwgc/bdwgc/releases/download/v${PV}/${MY_P}.tar.gz";
+S="${WORKDIR}/${MY_P}"
+
+LICENSE="boehm-gc"
+# SONAME: libgc.so.1 libgccpp.so.1
+# We've been using subslot 0 for these instead of "1.1".
+SLOT="0"
+# Don't keyword versions if upstream mark them as pre-release.
+KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~loong ~mips ~ppc ~ppc64 ~riscv 
~s390 ~sparc ~x86 ~amd64-linux ~x86-linux ~arm64-macos ~x64-macos ~x64-solaris"
+IUSE="cxx +large static-libs +threads"
+
+RDEPEND=">=dev-libs/libatomic_ops-7.4[${MULTILIB_USEDEP}]"
+DEPEND="${RDEPEND}"
+BDEPEND="virtual/pkgconfig"
+
+PATCHES=(
+       "${FILESDIR}"/${PN}-8.2.10-crash.patch
+)
+
+src_prepare() {
+       default
+
+       # bug #594754
+       elibtoolize
+}
+
+src_configure() {
+       use static-libs && lto-guarantee-fat
+       multilib-minimal_src_configure
+}
+
+multilib_src_configure() {
+       local config=(
+               --disable-docs
+               --with-libatomic-ops
+               $(use_enable cxx cplusplus)
+               $(use_enable static-libs static)
+               $(use threads || echo --disable-threads)
+               $(use_enable large large-config)
+       )
+
+       ECONF_SOURCE="${S}" econf "${config[@]}"
+}
+
+multilib_src_install_all() {
+       local HTML_DOCS=( doc/*.md )
+       einstalldocs
+       dodoc doc/README{.environment,.linux,.macros}
+
+       # Package provides .pc files
+       find "${ED}" -name '*.la' -delete || die
+
+       newman doc/gc.man GC_malloc.1
+
+       strip-lto-bytecode
+}

diff --git a/dev-libs/boehm-gc/files/boehm-gc-8.2.10-crash.patch 
b/dev-libs/boehm-gc/files/boehm-gc-8.2.10-crash.patch
new file mode 100644
index 000000000000..be9986e52a96
--- /dev/null
+++ b/dev-libs/boehm-gc/files/boehm-gc-8.2.10-crash.patch
@@ -0,0 +1,258 @@
+https://github.com/bdwgc/bdwgc/issues/783
+https://github.com/bdwgc/bdwgc/issues/802
+https://github.com/bdwgc/bdwgc/commit/910b4f008f3bcb436a23a2d5f9784e5dd9ab66cd
+https://github.com/bdwgc/bdwgc/commit/1058994bc264dd99a7b6d3caefe0c129bfbd1fb7
+https://github.com/bdwgc/bdwgc/commit/73fdfdaf45fd35993c7f9e051875147ca39438c4
+
+From 910b4f008f3bcb436a23a2d5f9784e5dd9ab66cd Mon Sep 17 00:00:00 2001
+From: Ivan Maidanski <[email protected]>
+Date: Sat, 25 Oct 2025 23:19:33 +0300
+Subject: [PATCH] Fix SIGSEGV in remove_all_threads_but_me if fork from
+ unregistered thread (a cherry-pick of commit 44fbb3b9e from 'master')
+
+Issue #783 (bdwgc).
+
+Some clients call `fork()` from an unregistered thread, thus we might
+find no entry for the current thread in `GC_remove_all_threads_but_me`.
+
+* include/gc.h (GC_atfork_child): Add comment about `fork()` from
+an unregistered thread.
+* pthread_support.c [CAN_HANDLE_FORK] (GC_remove_all_threads_but_me):
+If `me` is null, then return immediately after loop (instead of `ABORT`
+or the assertion, regardless of `CPPCHECK` and `LINT2`).
+* win32_threads.c [CAN_HANDLE_FORK] (GC_remove_all_threads_but_me):
+Likewise.
+---
+ include/gc.h      |  1 +
+ pthread_support.c | 19 ++++++++-----------
+ win32_threads.c   |  4 +++-
+ 3 files changed, 12 insertions(+), 12 deletions(-)
+
+diff --git a/include/gc.h b/include/gc.h
+index 54d704a06..b929240fc 100644
+--- a/include/gc.h
++++ b/include/gc.h
+@@ -512,6 +512,7 @@ GC_API void GC_CALL GC_set_handle_fork(int);
+ /* non-zero); GC_atfork_child is to be called immediately in the child  */
+ /* branch (i.e., fork result is 0).  Note that GC_atfork_child() call   */
+ /* should, of course, precede GC_start_mark_threads call (if any).      */
++/* Note that fork() could be called from an unregistered thread.        */
+ GC_API void GC_CALL GC_atfork_prepare(void);
+ GC_API void GC_CALL GC_atfork_parent(void);
+ GC_API void GC_CALL GC_atfork_child(void);
+diff --git a/pthread_support.c b/pthread_support.c
+index dbde77f25..0efbcdd9c 100644
+--- a/pthread_support.c
++++ b/pthread_support.c
+@@ -829,11 +829,11 @@ GC_API void GC_CALL GC_register_altstack(void *stack, 
GC_word stack_size,
+     GC_threads[hv] = me;
+   }
+ 
+-/* 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 (if */
++/* any) 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.                                    */
+ STATIC void GC_remove_all_threads_but_me(void)
+ {
+     int hv;
+@@ -870,12 +870,9 @@ STATIC void GC_remove_all_threads_but_me(void)
+       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
++    if (NULL == me)
++      return; /* fork() is called from an unregistered thread */
++
+     /* 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();
+diff --git a/win32_threads.c b/win32_threads.c
+index d1b7d5dbe..0335085ed 100644
+--- a/win32_threads.c
++++ b/win32_threads.c
+@@ -1185,8 +1185,10 @@ GC_API void * GC_CALL GC_get_my_stackbottom(struct 
GC_stack_base *sb)
+         GC_threads[hv] = NULL;
+       }
+ 
++      if (NULL == me)
++        return; /* fork() is called from an unregistered thread */
++
+       /* Put "me" back to GC_threads.   */
+-      GC_ASSERT(me != NULL);
+       thread_id = GetCurrentThreadId(); /* differs from that in parent */
+       GC_threads[THREAD_TABLE_INDEX(thread_id)] = me;
+ 
+
+From 1058994bc264dd99a7b6d3caefe0c129bfbd1fb7 Mon Sep 17 00:00:00 2001
+From: Ivan Maidanski <[email protected]>
+Date: Thu, 30 Oct 2025 23:39:44 +0300
+Subject: [PATCH] Fix code defect of LOCK/UNLOCK in separate 'if' in
+ GC_generic_malloc_many (a cherry-pick of commit c7d342c83 from 'master')
+
+* mallocx.c [PARALLEL_MARK] (GC_generic_malloc_many): Check
+`GC_parallel` once around `GC_reclaim_generic()` call (so that
+nearby `UNLOCK()` and the following `LOCK()` to be placed inside
+a single `if` statement block).
+---
+ mallocx.c | 82 +++++++++++++++++++++++++++----------------------------
+ 1 file changed, 41 insertions(+), 41 deletions(-)
+
+diff --git a/mallocx.c b/mallocx.c
+index 340bd5231..927008958 100644
+--- a/mallocx.c
++++ b/mallocx.c
+@@ -374,54 +374,54 @@ GC_API void GC_CALL GC_generic_malloc_many(size_t lb, 
int k, void **result)
+                   ++ GC_fl_builder_count;
+                   UNLOCK();
+                   GC_release_mark_lock();
+-              }
+-#           endif
+-            op = GC_reclaim_generic(hbp, hhdr, lb,
+-                                    ok -> ok_init, 0, &my_bytes_allocd);
+-            if (op != 0) {
+-#             ifdef PARALLEL_MARK
+-                if (GC_parallel) {
+-                  *result = op;
+-                  (void)AO_fetch_and_add(&GC_bytes_allocd_tmp,
+-                                         (AO_t)my_bytes_allocd);
++
++                  op = GC_reclaim_generic(hbp, hhdr, lb, ok -> ok_init, NULL,
++                                          &my_bytes_allocd);
++                  if (op != NULL) {
++                      *result = op;
++                      (void)AO_fetch_and_add(&GC_bytes_allocd_tmp,
++                                             (AO_t)my_bytes_allocd);
++                      GC_acquire_mark_lock();
++                      -- GC_fl_builder_count;
++                      if (GC_fl_builder_count == 0) GC_notify_all_builder();
++#                     ifdef THREAD_SANITIZER
++                        GC_release_mark_lock();
++                        LOCK();
++                        GC_bytes_found += my_bytes_allocd;
++                        UNLOCK();
++#                     else
++                        GC_bytes_found += my_bytes_allocd;
++                                        /* The result may be inaccurate. */
++                        GC_release_mark_lock();
++#                     endif
++                      (void) GC_clear_stack(0);
++                      return;
++                  }
++
+                   GC_acquire_mark_lock();
+                   -- GC_fl_builder_count;
+                   if (GC_fl_builder_count == 0) GC_notify_all_builder();
+-#                 ifdef THREAD_SANITIZER
+-                    GC_release_mark_lock();
+-                    LOCK();
+-                    GC_bytes_found += my_bytes_allocd;
+-                    UNLOCK();
+-#                 else
+-                    GC_bytes_found += my_bytes_allocd;
+-                                        /* The result may be inaccurate. */
+-                    GC_release_mark_lock();
+-#                 endif
+-                  (void) GC_clear_stack(0);
+-                  return;
+-                }
+-#             endif
+-              /* We also reclaimed memory, so we need to adjust       */
+-              /* that count.                                          */
++                  GC_release_mark_lock();
++                  LOCK();
++                  /* GC lock is needed for reclaim list access.   We    */
++                  /* must decrement fl_builder_count before reacquiring */
++                  /* the lock.  Hopefully this path is rare.            */
++
++                  rlh = ok -> ok_reclaim_list; /* reload rlh after locking */
++                  if (NULL == rlh) break;
++                  continue;
++              }
++#           endif
++
++            op = GC_reclaim_generic(hbp, hhdr, lb, ok -> ok_init, NULL,
++                                    &my_bytes_allocd);
++            if (op != NULL) {
++              /* We also reclaimed memory, so we need to adjust */
++              /* that count.                                    */
+               GC_bytes_found += my_bytes_allocd;
+               GC_bytes_allocd += my_bytes_allocd;
+               goto out;
+             }
+-#           ifdef PARALLEL_MARK
+-              if (GC_parallel) {
+-                GC_acquire_mark_lock();
+-                -- GC_fl_builder_count;
+-                if (GC_fl_builder_count == 0) GC_notify_all_builder();
+-                GC_release_mark_lock();
+-                LOCK();
+-                /* GC lock is needed for reclaim list access.   We      */
+-                /* must decrement fl_builder_count before reacquiring   */
+-                /* the lock.  Hopefully this path is rare.              */
+-
+-                rlh = ok -> ok_reclaim_list; /* reload rlh after locking */
+-                if (NULL == rlh) break;
+-              }
+-#           endif
+         }
+     }
+     /* Next try to use prefix of global free list if there is one.      */
+From 73fdfdaf45fd35993c7f9e051875147ca39438c4 Mon Sep 17 00:00:00 2001
+From: Ivan Maidanski <[email protected]>
+Date: Fri, 31 Oct 2025 22:44:52 +0300
+Subject: [PATCH] Fix SIGSEGV if pthread_detach is called before collector
+ initialization (a cherry-pick of commit e40697e26 from 'master')
+
+The only case this seems to be needed is when the client calls
+`pthread_detach(pthread_self())` before the collector initialization.
+
+* pthread_support.c [!SN_TARGET_ORBIS && !SN_TARGET_PSP2]
+(GC_pthread_detach): Call `GC_init()` if the collector is not
+initialized; add comment.
+* win32_threads.c [GC_PTHREADS] (GC_pthread_detach): Likewise.
+---
+ pthread_support.c | 7 +++++++
+ win32_threads.c   | 1 +
+ 2 files changed, 8 insertions(+)
+
+diff --git a/pthread_support.c b/pthread_support.c
+index 0efbcdd9c..fb09f3d17 100644
+--- a/pthread_support.c
++++ b/pthread_support.c
+@@ -2016,6 +2016,13 @@ GC_INNER_PTHRSTART void GC_thread_exit_proc(void *arg)
+     DCL_LOCK_STATE;
+ 
+     INIT_REAL_SYMS();
++    if (!EXPECT(GC_is_initialized, TRUE)) {
++      /*
++       * The only case this seems to be needed is when the client calls
++       * pthread_detach(pthread_self()) before the collector initialization.
++       */
++      GC_init();
++    }
+     LOCK();
+     t = (GC_thread)COVERT_DATAFLOW(GC_lookup_thread(thread));
+     UNLOCK();
+diff --git a/win32_threads.c b/win32_threads.c
+index 0335085ed..0ec33cf95 100644
+--- a/win32_threads.c
++++ b/win32_threads.c
+@@ -3257,6 +3257,7 @@ GC_INNER void GC_thr_init(void)
+     GC_thread t;
+     DCL_LOCK_STATE;
+ 
++    if (!EXPECT(GC_is_initialized, TRUE)) GC_init();
+     GC_ASSERT(!GC_win32_dll_threads);
+     t = GC_lookup_pthread(thread);
+     result = pthread_detach(thread);

Reply via email to