From 8a8f8d72212d043469ef33549e8edc3bce244516 Mon Sep 17 00:00:00 2001 From: Vincent Fazio Date: Sat, 22 Feb 2025 19:43:26 -0600 Subject: [PATCH] Fix thread identifiers on 32-bit musl builds 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' Fix this by compiling in code targeting non-glibc based Linux platforms to cast the pthread_t to a uintptr and then to PyThread_ident_t. Upstream: https://github.com/python/cpython/pull/130391 [0]: https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Arrays-and-pointers-implementation.html Signed-off-by: Vincent Fazio --- Python/thread_pthread.h | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/Python/thread_pthread.h b/Python/thread_pthread.h index f588b4620da..84900c86401 100644 --- a/Python/thread_pthread.h +++ b/Python/thread_pthread.h @@ -307,6 +307,25 @@ do_start_joinable_thread(void (*func)(void *), void *arg, pthread_t* out_id) return 0; } +/* Helper to convert pthread_t to PyThread_ident_t. POSIX allows pthread_t to be + non-arithmetic, e.g., musl typedefs it as a pointer */ +static PyThread_ident_t +_pthread_t_to_ident(pthread_t value) { +#if SIZEOF_PTHREAD_T > SIZEOF_LONG + return (PyThread_ident_t) *(unsigned long *) &value; +#else + PyThread_ident_t ident; +#if defined(__linux__) && !defined(__GLIBC__) + ident = (PyThread_ident_t) (uintptr_t) value; + assert(pthread_equal(value, (pthread_t) (uintptr_t) ident)); +#else + ident = (PyThread_ident_t) value; + assert(pthread_equal(value, (pthread_t) ident)); +#endif + return ident; +#endif // SIZEOF_PTHREAD_T > SIZEOF_LONG +} + int PyThread_start_joinable_thread(void (*func)(void *), void *arg, PyThread_ident_t* ident, PyThread_handle_t* handle) { @@ -314,9 +333,8 @@ PyThread_start_joinable_thread(void (*func)(void *), void *arg, if (do_start_joinable_thread(func, arg, &th)) { return -1; } - *ident = (PyThread_ident_t) th; + *ident = _pthread_t_to_ident(th); *handle = (PyThread_handle_t) th; - assert(th == (pthread_t) *ident); assert(th == (pthread_t) *handle); return 0; } @@ -329,11 +347,7 @@ PyThread_start_new_thread(void (*func)(void *), void *arg) return PYTHREAD_INVALID_THREAD_ID; } pthread_detach(th); -#if SIZEOF_PTHREAD_T <= SIZEOF_LONG - return (unsigned long) th; -#else - return (unsigned long) *(unsigned long *) &th; -#endif + return (unsigned long) _pthread_t_to_ident(th);; } int @@ -358,8 +372,7 @@ PyThread_get_thread_ident_ex(void) { if (!initialized) PyThread_init_thread(); threadid = pthread_self(); - assert(threadid == (pthread_t) (PyThread_ident_t) threadid); - return (PyThread_ident_t) threadid; + return _pthread_t_to_ident(threadid); } unsigned long -- 2.34.1