Repository navigation
test_threading.test_recursion_limit() does crash (segfault) on Alpine Linux (musl) #148260
Description
Activity
- addedtestsTests in the Lib/test dirTests in the Lib/test dirinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
on Apr 8, 2026 Ah, I can also trigger the bug on a regular build (not Free Threaded) on Alpine Linux, Python built with:
./configure --with-pydebugandgcc -Og. I just have to increase the recursion limit to 2 000 frames:import threading import sys def recurse(): return recurse() def outer(): try: recurse() except RecursionError: pass sys.setrecursionlimit(2_000) w = threading.Thread(target=outer) w.start() w.join() print('end of main thread')
I get a similar gdb traceback:
#0 0x0000560e899e7155 in _PyObject_GC_UNTRACK (filename=filename@entry=0x560e89af12ee "Python/gc.c", lineno=lineno@entry=2382, op=op@entry=0x7f5d79f06170) at ./Include/internal/pycore_gc.h:279 #1 0x0000560e899e9442 in PyObject_GC_UnTrack (op_raw=op_raw@entry=0x7f5d79f06170) at Python/gc.c:2382 #2 0x0000560e89a49d11 in tb_dealloc (op=0x7f5d79f06170) at Python/traceback.c:244 #3 0x0000560e898a60b6 in _Py_Dealloc (op=op@entry=0x7f5d79f06170) at Objects/object.c:3300 #4 0x0000560e89a49b28 in Py_DECREF (filename=filename@entry=0x560e89ade25b "./Include/refcount.h", lineno=lineno@entry=520, op=0x7f5d79f06170) at ./Include/refcount.h:410 #5 0x0000560e89a49b47 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520 #6 0x0000560e89a49d1a in tb_dealloc (op=0x7f5d79f061c0) at Python/traceback.c:245 #7 0x0000560e898a60b6 in _Py_Dealloc (op=op@entry=0x7f5d79f061c0) at Objects/object.c:3300 #8 0x0000560e89a49b28 in Py_DECREF (filename=filename@entry=0x560e89ade25b "./Include/refcount.h", lineno=lineno@entry=520, op=0x7f5d79f061c0) at ./Include/refcount.h:410 #9 0x0000560e89a49b47 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520 #10 0x0000560e89a49d1a in tb_dealloc (op=0x7f5d79f06210) at Python/traceback.c:245 #11 0x0000560e898a60b6 in _Py_Dealloc (op=op@entry=0x7f5d79f06210) at Objects/object.c:3300 #12 0x0000560e89a49b28 in Py_DECREF (filename=filename@entry=0x560e89ade25b "./Include/refcount.h", lineno=lineno@entry=520, op=0x7f5d79f06210) at ./Include/refcount.h:410 #13 0x0000560e89a49b47 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520 #14 0x0000560e89a49d1a in tb_dealloc (op=0x7f5d79f06260) at Python/traceback.c:245 #15 0x0000560e898a60b6 in _Py_Dealloc (op=op@entry=0x7f5d79f06260) at Objects/object.c:3300 #16 0x0000560e89a49b28 in Py_DECREF (filename=filename@entry=0x560e89ade25b "./Include/refcount.h", lineno=lineno@entry=520, op=0x7f5d79f06260) at ./Include/refcount.h:410 #17 0x0000560e89a49b47 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520 #18 0x0000560e89a49d1a in tb_dealloc (op=0x7f5d79f062b0) at Python/traceback.c:245 #19 0x0000560e898a60b6 in _Py_Dealloc (op=op@entry=0x7f5d79f062b0) at Objects/object.c:3300 #20 0x0000560e89a49b28 in Py_DECREF (filename=filename@entry=0x560e89ade25b "./Include/refcount.h", lineno=lineno@entry=520, op=0x7f5d79f062b0) at ./Include/refcount.h:410 #21 0x0000560e89a49b47 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520 #22 0x0000560e89a49d1a in tb_dealloc (op=0x7f5d79f06300) at Python/traceback.c:245 #23 0x0000560e898a60b6 in _Py_Dealloc (op=op@entry=0x7f5d79f06300) at Objects/object.c:3300 #24 0x0000560e89a49b28 in Py_DECREF (filename=filename@entry=0x560e89ade25b "./Include/refcount.h", lineno=lineno@entry=520, op=0x7f5d79f06300) at ./Include/refcount.h:410 #25 0x0000560e89a49b47 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520 #26 0x0000560e89a49d1a in tb_dealloc (op=0x7f5d79f06350) at Python/traceback.c:245 #27 0x0000560e898a60b6 in _Py_Dealloc (op=op@entry=0x7f5d79f06350) at Objects/object.c:3300 ... #4128 0x0000560e8984887b in PyObject_Call (callable=<optimized out>, args=<optimized out>, kwargs=<optimized out>) at Objects/call.c:373 #4129 0x0000560e89aca755 in thread_run (boot_raw=boot_raw@entry=0x7f5d7a1cdd50) at ./Modules/_threadmodule.c:388 #4130 0x0000560e89a485c6 in pythread_wrapper (arg=<optimized out>) at Python/thread_pthread.h:234 #4131 0x00007f5d7a3e9573 in start (p=<optimized out>) at src/thread/pthread_create.c:207 #4132 0x00007f5d7a3eaec1 in __clone () at src/thread/x86_64/clone.s:22 Backtrace stopped: frame did not save the PCFirst
_Py_Dealloc()call:(gdb) p type->tp_name $1 = 0x560e89ae4d03 "RecursionError" (gdb) p type->tp_flags & (1UL << 14) # gc_flag $3 = 16384 (gdb) p margin $4 = 119Last
_Py_Dealloc()call:(gdb) p type->tp_name $5 = 0x560e89af0a40 "traceback" (gdb) p type->tp_flags & (1UL << 14) # gc_flag $7 = 16384 (gdb) p margin $8 = 115- changed the title
[-]test_threading.test_recursion_limit() does crash (segfault) on Alpine with Free Threaded build[/-][+]test_threading.test_recursion_limit() does crash (segfault) on Alpine Linux (musl)[/+]on Apr 8, 2026 https://wiki.musl-libc.org/functional-differences-from-glibc.html says:
musl provides a default thread stack size of 128k
On my Linux x86-64 (Fedora 43) with glibc, I get a default stack size of 8 MiB.
We can add
-Wl,-z,stack-size=1048576flag toLDFLAGS_NODISTorLINKFORSHAREDto increase the thread stack size to 1 MiB.See also commit 1e9b8f2 (PR gh-134336) which modified
Python/ceval.cto no longer usepthread_attr_getstack()on musl.I tried using
$ac_cv_libcvariable in configure.ac, but this variable is unset: #143730 (comment).It doesn't seem possible to detect that Python will be linked to musl C library. Maybe another option would be to query the thread stack size to decide to pass
-Wl,-z,stack-size=1048576option to the linker or not.Example of C program which returns exit code 1 if the thread stack size is smaller than 1 MiB:
#include <pthread.h> #include <assert.h> size_t get(void) { pthread_attr_t attrs; size_t size; int rc = pthread_attr_init(&attrs); assert(rc == 0); rc = pthread_attr_getstacksize(&attrs, &size); assert(rc == 0); return size; } int main() { size_t size = get(); if (size < 1024 * 1024) { return 1; } return 0; }I wrote PR gh-149993 which adds
-Wl,-z,stack-size=1048576linker flag when Python is linked to the musl C library.- added a commit that references this issue
on May 20, 2026 I increased the minimum thread stack size to 1 MiB, whereas musl uses 128 kiB by default. If 1 MiB is not used (glibc uses 8 MiB on Linux!), we can increase the stack size later.
- added a commit that references this issue
on Aug 22, 2026
Python built with
./configure --with-pydebug --disable-gilandgcc -Ogon Alpine Linux (Python built withmuslC library).Example of crash on "AMD64 Alpine Linux NoGIL 3.x" buildbot: https://buildbot.python.org/#/builders/1819/builds/1699
Script:
gdb traceback:
_Py_Dealloc()has theTrashcanmechanism which is supposed to avoid long chain of_Py_Dealloc()calls to reduce the stack memory consumption. But it seems like it doesn't work on Alpine Linux (Python built withmuslC library).gdb variables in the
_Py_Dealloc()frame:In the parent
_Py_Dealloc()frame:In the first
_Py_Dealloc()frame (outer):On a regular build (without Free Threading), the script works as expected, it displays:
Linked PRs