Skip to content

Segfault by calling repr(SimpleNamespace) with typing.Union attributes in threads on a free-threading build #135878

Open
@devdanzin

Description

@devdanzin

Crash report

What happened?

It's possible to segfault or abort the interpreter of a free-threading build by calling repr(SimpleNamespace) in threads when the instance has typing.Union attributes. This seems very similar to #132713.

The MRE will rarely hang and is non-deterministic, but is the best I was able to come up with.

MRE:

from functools import reduce
from operator import or_
from threading import Thread

from types import SimpleNamespace

all_types = []
for x in range(400):
    class Dummy: pass
    all_types.append(Dummy)
big_union = reduce(or_, all_types, int)

for x in range(500):
    s = SimpleNamespace()

    def stress_simplenamespace():
        for x in range(20):
            varying_union = big_union | tuple[int, ...] | list[str, ...] | dict[str, int]
            repr(s)
            attr_name = f"t_attr_{x}"
            setattr(s, attr_name, varying_union)
            repr(s)
            repr(s)
            repr(s)

    alive = [Thread(target=stress_simplenamespace) for x in range(8)]
    for t in alive:
        t.start()
    for t in alive:
        t.join()

Segfault backtrace 1:

Thread 7 "Thread-6 (stres" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff541b640 (LWP 348490)]
0x000055555567cdcd in setup_ga (args=<unknown at remote 0x7ffff5c20640>, origin=<type at remote 0x555555b11d40>, alias=0x2276a0e0140) at Objects/genericaliasobject.c:837
837         if (!PyTuple_Check(args)) {

#0  0x000055555567cdcd in setup_ga (args=<unknown at remote 0x7ffff5c20640>, origin=<type at remote 0x555555b11d40>, alias=0x2276a0e0140)
    at Objects/genericaliasobject.c:837
#1  Py_GenericAlias (origin=<type at remote 0x555555b11d40>, args=<unknown at remote 0x7ffff5c20640>) at Objects/genericaliasobject.c:1015
#2  0x0000555555658dd5 in _PyObject_VectorcallTstate (kwnames=0x0, nargsf=9223372036854775809, args=0x7ffff541a428,
    callable=<built-in method __class_getitem__ of type object at remote 0x555555b11d40>, tstate=0x555555c3c6e0)
    at ./Include/internal/pycore_call.h:169
#3  PyObject_CallOneArg (func=<built-in method __class_getitem__ of type object at remote 0x555555b11d40>,
    arg=arg@entry=<unknown at remote 0x7ffff5c20640>) at Objects/call.c:395
#4  0x0000555555634781 in PyObject_GetItem (o=<type at remote 0x555555b11d40>, key=<unknown at remote 0x7ffff5c20640>) at Objects/abstract.c:193
#5  0x00005555555e2f02 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>)
    at Python/generated_cases.c.h:62
#6  0x00005555557dd13e in _PyEval_EvalFrame (throwflag=0, frame=<optimized out>, tstate=0x555555c3c6e0) at ./Include/internal/pycore_ceval.h:119
#7  _PyEval_Vector (tstate=0x555555c3c6e0, func=0x2275e37fd80, locals=0x0, args=0x7ffff541a8a8, argcount=<optimized out>, kwnames=<optimized out>)
    at Python/ceval.c:1975

Segfault backtrace 2:

Thread 3 "Thread-2 (stres" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff742f640 (LWP 343364)]
clear_freelist (dofree=<optimized out>, is_finalization=<optimized out>, freelist=<optimized out>) at Objects/object.c:904
904             dofree(ptr);

#0  clear_freelist (dofree=<optimized out>, is_finalization=<optimized out>, freelist=<optimized out>) at Objects/object.c:904
#1  _PyObject_ClearFreeLists (freelists=0x555555c30948, is_finalization=is_finalization@entry=1) at Objects/object.c:929
#2  0x000055555585c521 in PyThreadState_Clear (tstate=tstate@entry=0x555555c2cf60) at Python/pystate.c:1703
#3  0x0000555555904a10 in thread_run (boot_raw=0x555555c194a0) at ./Modules/_threadmodule.c:390
#4  0x000055555587e27b in pythread_wrapper (arg=<optimized out>) at Python/thread_pthread.h:232
#5  0x00007ffff7d31ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#6  0x00007ffff7dc3850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

A backtrace for a segfault I got with a variant of this code, seems more informative but I cannot repro with the MRE:

Thread 185 "asyncio_4" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xffffe31ff060 (LWP 1527140)]
0x0000000000867ce8 in _Py_TYPE (ob=<unknown at remote 0xdddddddddddddddd>) at ./Include/object.h:270
270             return ob->ob_type;

#0  0x0000000000867ce8 in _Py_TYPE (ob=<unknown at remote 0xdddddddddddddddd>) at ./Include/object.h:270
#1  union_repr (self=<unknown at remote 0xffffa425a9d0>) at Objects/unionobject.c:296
#2  0x00000000006e61a8 in PyObject_Repr (v=<unknown at remote 0xffffa425a9d0>) at Objects/object.c:779
#3  0x000000000082ada0 in unicode_fromformat_arg (writer=writer@entry=0xffffe31fb4e0, f=0xd88224 "R", f@entry=0xd88223 "%R", vargs=vargs@entry=0xffffe31fb410)
    at Objects/unicodeobject.c:3101
#4  0x000000000082b33c in unicode_from_format (writer=writer@entry=0xffffe31fb4e0, format=format@entry=0xd88220 "%U=%R", 
    vargs=<error reading variable: Cannot access memory at address 0x1>) at Objects/unicodeobject.c:3220
#5  0x000000000082b68c in PyUnicode_FromFormatV (format=format@entry=0xd88220 "%U=%R", vargs=...) at Objects/unicodeobject.c:3254
#6  0x000000000082b828 in PyUnicode_FromFormat (format=format@entry=0xd88220 "%U=%R") at Objects/unicodeobject.c:3268
#7  0x00000000006e1038 in namespace_repr (ns=<types.SimpleNamespace at remote 0xffffa48e3c60>) at Objects/namespaceobject.c:129
#8  0x000000000075e894 in object_str (self=self@entry=<types.SimpleNamespace at remote 0xffffa48e3c60>) at Objects/typeobject.c:7152
#9  0x0000000000768d18 in wrap_unaryfunc (self=<types.SimpleNamespace at remote 0xffffa48e3c60>, args=<optimized out>, wrapped=0x75e854 <object_str>)
    at Objects/typeobject.c:9536
#10 0x00000000005d8ee0 in wrapperdescr_raw_call (kwds=<optimized out>, args=<optimized out>, self=<optimized out>, descr=<optimized out>) at Objects/descrobject.c:532
#11 wrapper_call (self=<optimized out>, args=<optimized out>, kwds=<optimized out>) at Objects/descrobject.c:1437
#12 0x00000000005ab38c in _PyObject_MakeTpCall (tstate=tstate@entry=0xffff48937210, 
    callable=callable@entry=<method-wrapper '__str__' of types.SimpleNamespace object at 0xffffa48e3c60>, args=args@entry=0xffffe31fbe58, nargs=nargs@entry=0, 
    keywords=keywords@entry=0x0) at Objects/call.c:242
#13 0x00000000005aba68 in _PyObject_VectorcallTstate (tstate=0xffff48937210, callable=<method-wrapper '__str__' of types.SimpleNamespace object at 0xffffa48e3c60>, 
    args=0xffffe31fbe58, nargsf=9223372036854775808, kwnames=0x0) at ./Include/internal/pycore_call.h:167
#14 0x00000000005aba98 in PyObject_Vectorcall (callable=callable@entry=<method-wrapper '__str__' of types.SimpleNamespace object at 0xffffa48e3c60>, 
    args=args@entry=0x0, nargsf=nargsf@entry=9223372036854775808, kwnames=kwnames@entry=0x0) at Objects/call.c:327

Abort backtrace:

*** stack smashing detected ***: terminated

Thread 7 "Thread-6 (stres" received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffff541b640 (LWP 351627)]
__pthread_kill_implementation (no_tid=0, signo=6, threadid=0) at ./nptl/pthread_kill.c:44

#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=0) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=0) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=0, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7cdf476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7cc57f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff7d26677 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff7e7892e "*** %s ***: terminated\n")
    at ../sysdeps/posix/libc_fatal.c:156
#6  0x00007ffff7dd359a in __GI___fortify_fail (msg=msg@entry=0x7ffff7e78916 "stack smashing detected") at ./debug/fortify_fail.c:26
#7  0x00007ffff7dd3566 in __stack_chk_fail () at ./debug/stack_chk_fail.c:24
#8  0x000055555572321d in _Py_type_getattro_impl (type=<optimized out>, name=<optimized out>,
    suppress_missing_attribute=suppress_missing_attribute@entry=0x7ffff541a194) at Objects/typeobject.c:6355
#9  0x00005555556db432 in PyObject_GetOptionalAttr (v=v@entry=<type at remote 0x3b2b2e06800>, name=<optimized out>,
    result=result@entry=0x7ffff541a1d0) at Objects/object.c:1345
#10 0x000055555572ea38 in _Py_typing_type_repr (writer=writer@entry=0x3b2be090e80, p=<type at remote 0x3b2b2e06800>) at Objects/typevarobject.c:295
#11 0x000055555577ad6e in union_repr (self=<typing.Union at remote 0x3b2c20e0820>) at Objects/unionobject.c:297
#12 0x00005555556d80db in PyObject_Repr (v=<typing.Union at remote 0x3b2c20e0820>) at ./Include/object.h:277
#13 PyObject_Repr (v=<typing.Union at remote 0x3b2c20e0820>) at Objects/object.c:754
#14 0x0000555555765835 in unicode_fromformat_arg (vargs=0x7ffff541a278, f=0x555555937b12 "R", writer=0x7ffff541a300)
    at Objects/unicodeobject.c:3101
#15 unicode_from_format (writer=writer@entry=0x7ffff541a300, format=format@entry=0x555555937b0e "%U=%R", vargs=vargs@entry=0x7ffff541a340)
    at Objects/unicodeobject.c:3220
#16 0x000055555576fadf in PyUnicode_FromFormatV (vargs=0x7ffff541a340, format=0x555555937b0e "%U=%R") at Objects/unicodeobject.c:3254
#17 PyUnicode_FromFormat (format=format@entry=0x555555937b0e "%U=%R") at Objects/unicodeobject.c:3268
#18 0x00005555556d5802 in namespace_repr (ns=<types.SimpleNamespace at remote 0x3b2b2152310>) at Objects/namespaceobject.c:129
#19 0x00005555556d80db in PyObject_Repr (v=<types.SimpleNamespace at remote 0x3b2b2152310>) at ./Include/object.h:277
#20 PyObject_Repr (v=<types.SimpleNamespace at remote 0x3b2b2152310>) at Objects/object.c:754
#21 0x00005555555e4883 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>)
    at Python/generated_cases.c.h:2487
#22 0x00005555557dd13e in _PyEval_EvalFrame (throwflag=0, frame=<optimized out>, tstate=0x555555c3c6e0) at ./Include/internal/pycore_ceval.h:119
#23 _PyEval_Vector (tstate=0x555555c3c6e0, func=0x3b2b237fd80, locals=0x0, args=0x7ffff541a8a8, argcount=<optimized out>, kwnames=<optimized out>)
    at Python/ceval.c:1975

Found using fusil by @vstinner.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.15.0a0 experimental free-threading build (heads/main:b706ff003c5, Jun 11 2025, 19:34:04) [GCC 11.4.0]

Linked PRs

Metadata

Metadata

Assignees

Labels

interpreter-core(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-crashA hard crash of the interpreter, possibly with a core dump

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions