Skip to content

Commit 8f83c2f

Browse files
authored
bpo-33015: Fix UB in pthread PyThread_start_new_thread (GH-6008) (GH-10823)
Fix an undefined behaviour in the pthread implementation of PyThread_start_new_thread(): add a function wrapper to always return NULL. Add pythread_callback struct and pythread_wrapper() to thread_pthread.h. (cherry picked from commit 9eea6ea)
1 parent dab59fa commit 8f83c2f

File tree

2 files changed

+39
-4
lines changed

2 files changed

+39
-4
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix an undefined behaviour in the pthread implementation of
2+
:c:func:`PyThread_start_new_thread`: add a function wrapper to always return
3+
``NULL``.

Python/thread_pthread.h

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,28 @@ PyThread__init_thread(void)
156156
* Thread support.
157157
*/
158158

159+
/* bpo-33015: pythread_callback struct and pythread_wrapper() cast
160+
"void func(void *)" to "void* func(void *)": always return NULL.
161+
162+
PyThread_start_new_thread() uses "void func(void *)" type, whereas
163+
pthread_create() requires a void* return value. */
164+
typedef struct {
165+
void (*func) (void *);
166+
void *arg;
167+
} pythread_callback;
168+
169+
static void *
170+
pythread_wrapper(void *arg)
171+
{
172+
/* copy func and func_arg and free the temporary structure */
173+
pythread_callback *callback = arg;
174+
void (*func)(void *) = callback->func;
175+
void *func_arg = callback->arg;
176+
PyMem_Free(arg);
177+
178+
func(func_arg);
179+
return NULL;
180+
}
159181

160182
long
161183
PyThread_start_new_thread(void (*func)(void *), void *arg)
@@ -191,21 +213,31 @@ PyThread_start_new_thread(void (*func)(void *), void *arg)
191213
pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM);
192214
#endif
193215

216+
pythread_callback *callback = PyMem_Malloc(sizeof(pythread_callback));
217+
218+
if (callback == NULL) {
219+
return -1;
220+
}
221+
222+
callback->func = func;
223+
callback->arg = arg;
224+
194225
status = pthread_create(&th,
195226
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
196227
&attrs,
197228
#else
198229
(pthread_attr_t*)NULL,
199230
#endif
200-
(void* (*)(void *))func,
201-
(void *)arg
202-
);
231+
pythread_wrapper, callback);
203232

204233
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
205234
pthread_attr_destroy(&attrs);
206235
#endif
207-
if (status != 0)
236+
237+
if (status != 0) {
238+
PyMem_Free(callback);
208239
return -1;
240+
}
209241

210242
pthread_detach(th);
211243

0 commit comments

Comments
 (0)