1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
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 <ivmai@mail.ru>
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 <ivmai@mail.ru>
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 <ivmai@mail.ru>
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);
|