Skip to content

Commit 57bb72e

Browse files
vfaziomiss-islington
authored andcommitted
gh-130115: fix thread identifiers for 32-bit musl (GH-130391)
CPython's pthread-based thread identifier relies on pthread_t being able to be represented as an unsigned integer type. This is true in most Linux libc implementations where it's defined as an unsigned long, however musl typedefs it as a struct *. If the pointer has the high bit set and is cast to PyThread_ident_t, the resultant value can be sign-extended [0]. This can cause issues when comparing against threading._MainThread's identifier. The main thread's identifier value is retrieved via _get_main_thread_ident which is backed by an unsigned long which truncates sign extended bits. >>> hex(threading.main_thread().ident) '0xb6f33f3c' >>> hex(threading.current_thread().ident) '0xffffffffb6f33f3c' Work around this by conditionally compiling in some code for non-glibc based Linux platforms that are at risk of sign-extension to return a PyLong based on the main thread's unsigned long thread identifier if the current thread is the main thread. [0]: https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html --------- (cherry picked from commit 7212306) Co-authored-by: Vincent Fazio <[email protected]> Signed-off-by: Vincent Fazio <[email protected]>
1 parent d30052a commit 57bb72e

File tree

2 files changed

+22
-9
lines changed

2 files changed

+22
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix an issue with thread identifiers being sign-extended on some platforms.

Python/thread_pthread.h

+21-9
Original file line numberDiff line numberDiff line change
@@ -307,16 +307,33 @@ do_start_joinable_thread(void (*func)(void *), void *arg, pthread_t* out_id)
307307
return 0;
308308
}
309309

310+
/* Helper to convert pthread_t to PyThread_ident_t. POSIX allows pthread_t to be
311+
non-arithmetic, e.g., musl typedefs it as a pointer. */
312+
static PyThread_ident_t
313+
_pthread_t_to_ident(pthread_t value) {
314+
// Cast through an integer type of the same size to avoid sign-extension.
315+
#if SIZEOF_PTHREAD_T == SIZEOF_VOID_P
316+
return (uintptr_t) value;
317+
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG
318+
return (unsigned long) value;
319+
#elif SIZEOF_PTHREAD_T == SIZEOF_INT
320+
return (unsigned int) value;
321+
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG_LONG
322+
return (unsigned long long) value;
323+
#else
324+
#error "Unsupported SIZEOF_PTHREAD_T value"
325+
#endif
326+
}
327+
310328
int
311329
PyThread_start_joinable_thread(void (*func)(void *), void *arg,
312330
PyThread_ident_t* ident, PyThread_handle_t* handle) {
313331
pthread_t th = (pthread_t) 0;
314332
if (do_start_joinable_thread(func, arg, &th)) {
315333
return -1;
316334
}
317-
*ident = (PyThread_ident_t) th;
335+
*ident = _pthread_t_to_ident(th);
318336
*handle = (PyThread_handle_t) th;
319-
assert(th == (pthread_t) *ident);
320337
assert(th == (pthread_t) *handle);
321338
return 0;
322339
}
@@ -329,11 +346,7 @@ PyThread_start_new_thread(void (*func)(void *), void *arg)
329346
return PYTHREAD_INVALID_THREAD_ID;
330347
}
331348
pthread_detach(th);
332-
#if SIZEOF_PTHREAD_T <= SIZEOF_LONG
333-
return (unsigned long) th;
334-
#else
335-
return (unsigned long) *(unsigned long *) &th;
336-
#endif
349+
return (unsigned long) _pthread_t_to_ident(th);;
337350
}
338351

339352
int
@@ -358,8 +371,7 @@ PyThread_get_thread_ident_ex(void) {
358371
if (!initialized)
359372
PyThread_init_thread();
360373
threadid = pthread_self();
361-
assert(threadid == (pthread_t) (PyThread_ident_t) threadid);
362-
return (PyThread_ident_t) threadid;
374+
return _pthread_t_to_ident(threadid);
363375
}
364376

365377
unsigned long

0 commit comments

Comments
 (0)