Skip to content

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

Closed
@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

    pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy