Skip to content

test_threading.test_recursion_limit() does crash (segfault) on Alpine with Free Threaded build #148260

@vstinner

Description

@vstinner

Python built with ./configure --with-pydebug --disable-gil and gcc -Og on Alpine Linux (Python built with musl C library).

Example of crash on "AMD64 Alpine Linux NoGIL 3.x" buildbot: https://buildbot.python.org/#/builders/1819/builds/1699

Script:

import threading

def recurse():
    return recurse()

def outer():
    try:
        recurse()
    except RecursionError:
        pass

w = threading.Thread(target=outer)
w.start()
w.join()
print('end of main thread')

gdb traceback:

(gdb) where
#0  0x0000560e9bedb871 in _Py_Dealloc (op=0x20004156010) at Objects/object.c:3277
#1  0x0000560e9bedb9ef in _Py_MergeZeroLocalRefcount (op=op@entry=0x20004156010) at Objects/object.c:444
#2  0x0000560e9c088aaf in Py_DECREF (filename=filename@entry=0x560e9c12425b "./Include/refcount.h", 
    lineno=lineno@entry=520, op=0x20004156010) at ./Include/refcount.h:359
#3  0x0000560e9c088af7 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520
#4  0x0000560e9c088e30 in tb_dealloc (op=0x20004156070) at Python/traceback.c:245
#5  0x0000560e9bedb8e0 in _Py_Dealloc (op=0x20004156070) at Objects/object.c:3300
#6  0x0000560e9bedb9ef in _Py_MergeZeroLocalRefcount (op=op@entry=0x20004156070) at Objects/object.c:444
#7  0x0000560e9c088aaf in Py_DECREF (filename=filename@entry=0x560e9c12425b "./Include/refcount.h", 
    lineno=lineno@entry=520, op=0x20004156070) at ./Include/refcount.h:359
#8  0x0000560e9c088af7 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520
#9  0x0000560e9c088e30 in tb_dealloc (op=0x200041560d0) at Python/traceback.c:245
#10 0x0000560e9bedb8e0 in _Py_Dealloc (op=0x200041560d0) at Objects/object.c:3300
#11 0x0000560e9bedb9ef in _Py_MergeZeroLocalRefcount (op=op@entry=0x200041560d0) at Objects/object.c:444
#12 0x0000560e9c088aaf in Py_DECREF (filename=filename@entry=0x560e9c12425b "./Include/refcount.h", 
    lineno=lineno@entry=520, op=0x200041560d0) at ./Include/refcount.h:359
#13 0x0000560e9c088af7 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520
#14 0x0000560e9c088e30 in tb_dealloc (op=0x20004156130) at Python/traceback.c:245
#15 0x0000560e9bedb8e0 in _Py_Dealloc (op=0x20004156130) at Objects/object.c:3300
#16 0x0000560e9bedb9ef in _Py_MergeZeroLocalRefcount (op=op@entry=0x20004156130) at Objects/object.c:444
#17 0x0000560e9c088aaf in Py_DECREF (filename=filename@entry=0x560e9c12425b "./Include/refcount.h", 
    lineno=lineno@entry=520, op=0x20004156130) at ./Include/refcount.h:359
#18 0x0000560e9c088af7 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520
#19 0x0000560e9c088e30 in tb_dealloc (op=0x20004156190) at Python/traceback.c:245
#20 0x0000560e9bedb8e0 in _Py_Dealloc (op=0x20004156190) at Objects/object.c:3300
#21 0x0000560e9bedb9ef in _Py_MergeZeroLocalRefcount (op=op@entry=0x20004156190) at Objects/object.c:444
#22 0x0000560e9c088aaf in Py_DECREF (filename=filename@entry=0x560e9c12425b "./Include/refcount.h", 
    lineno=lineno@entry=520, op=0x20004156190) at ./Include/refcount.h:359
#23 0x0000560e9c088af7 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520
#24 0x0000560e9c088e30 in tb_dealloc (op=0x200041561f0) at Python/traceback.c:245
#25 0x0000560e9bedb8e0 in _Py_Dealloc (op=0x200041561f0) at Objects/object.c:3300
#26 0x0000560e9bedb9ef in _Py_MergeZeroLocalRefcount (op=op@entry=0x200041561f0) at Objects/object.c:444
#27 0x0000560e9c088aaf in Py_DECREF (filename=filename@entry=0x560e9c12425b "./Include/refcount.h", 
    lineno=lineno@entry=520, op=0x200041561f0) at ./Include/refcount.h:359
#28 0x0000560e9c088af7 in Py_XDECREF (op=<optimized out>) at ./Include/refcount.h:520
#29 0x0000560e9c088e30 in tb_dealloc (op=0x20004156250) at Python/traceback.c:245
#30 0x0000560e9bedb8e0 in _Py_Dealloc (op=0x20004156250) at Objects/object.c:3300
#31 0x0000560e9bedb9ef in _Py_MergeZeroLocalRefcount (op=op@entry=0x20004156250) at Objects/object.c:444
#32 0x0000560e9c088aaf in Py_DECREF (filename=filename@entry=0x560e9c12425b "./Include/refcount.h", 
(...)
#3751 0x0000560e9be774fb in PyObject_Call (callable=<optimized out>, args=<optimized out>, kwargs=<optimized out>)
    at Objects/call.c:373
#3752 0x0000560e9c10f6ef in thread_run (boot_raw=boot_raw@entry=0x20000030920) at ./Modules/_threadmodule.c:388
#3753 0x0000560e9c0874c0 in pythread_wrapper (arg=<optimized out>) at Python/thread_pthread.h:234
#3754 0x00007f484b327573 in start (p=<optimized out>) at src/thread/pthread_create.c:207
#3755 0x00007f484b328ec1 in __clone () at src/thread/x86_64/clone.s:22
Backtrace stopped: frame did not save the PC

(gdb) info threads
  Id   Target Id                  Frame 
  1    LWP 9804 "python"          __cp_end () at src/thread/x86_64/syscall_cp.s:29
* 2    LWP 9807 "Thread-1 (outer" 0x0000560e9bedb871 in _Py_Dealloc (op=0x20004156010) at Objects/object.c:3277

_Py_Dealloc() has the Trashcan mechanism 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 with musl C library).

gdb variables in the _Py_Dealloc() frame:

(gdb) p type->tp_name
$6 = 0x555ba66f5c7f "traceback"

(gdb) p gc_flag
$7 = 16384

In the parent _Py_Dealloc() frame:

(gdb) p type->tp_name
$9 = 0x555ba66f5c7f "traceback"
(gdb) p gc_flag
$10 = <optimized out>
(gdb) p margin
$11 = 115

In the first _Py_Dealloc() frame (outer):

(gdb) p margin
$12 = 119
(gdb) p type->tp_name
$13 = 0x555ba66e9dcd "RecursionError"
(gdb) p gc_flag
$14 = <optimized out>
(gdb) p type->tp_flags & (1UL << 14)  # gc_flag
$15 = 16384

On a regular build (without Free Threading), the script works as expected, it displays:

end of main thread

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions