[3.13] gh-130115: fix thread identifiers for 32-bit musl (GH-130391) (GH-132089)

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 72123063dd)

Signed-off-by: Vincent Fazio <vfazio@gmail.com>
Co-authored-by: Vincent Fazio <vfazio@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-04-04 22:57:35 +02:00 committed by GitHub
parent 53752d20c4
commit 240c200cce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 22 additions and 9 deletions

View file

@ -0,0 +1 @@
Fix an issue with thread identifiers being sign-extended on some platforms.

View file

@ -307,6 +307,24 @@ 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) {
// Cast through an integer type of the same size to avoid sign-extension.
#if SIZEOF_PTHREAD_T == SIZEOF_VOID_P
return (uintptr_t) value;
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG
return (unsigned long) value;
#elif SIZEOF_PTHREAD_T == SIZEOF_INT
return (unsigned int) value;
#elif SIZEOF_PTHREAD_T == SIZEOF_LONG_LONG
return (unsigned long long) value;
#else
#error "Unsupported SIZEOF_PTHREAD_T value"
#endif
}
int
PyThread_start_joinable_thread(void (*func)(void *), void *arg,
PyThread_ident_t* ident, PyThread_handle_t* handle) {
@ -314,9 +332,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 +346,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 +371,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