Skip to content

Crash in _io_TextIOWrapper_write_impl when running tracemalloc.clear_traces in threads #144763

@devdanzin

Description

@devdanzin

Crash report

What happened?

Running the code below will non-deterministically cause a segfault or abort in _io_TextIOWrapper_write_impl in the !PyList_CheckExact(self->pending_bytes) assertion.

from sys import stdout
from threading import Thread
from time import sleep
from tracemalloc import clear_traces, start, get_traceback_limit

def print_to_file(text):
    get_traceback_limit()
    stdout.write("")
    clear_traces()
    sleep(0.04)
    clear_traces()

alive = []

start()
for i in range(300):
    alive.append(Thread(target=print_to_file, args=("a" * 800,)))

for t in alive:
    t.start()
    print(object)
Segfault backtrace

Thread 143 "Thread-142 (pri" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7bfec37d66c0 (LWP 1476230)]
0x000055555622f434 in _Py_TYPE_impl (ob=0x0) at ./Include/object.h:313
313         return ob->ob_type;

#0  0x000055555622f434 in _Py_TYPE_impl (ob=0x0) at ./Include/object.h:313
#1  _Py_IS_TYPE_impl (ob=0x0, type=<optimized out>) at ./Include/object.h:328
#2  _io_TextIOWrapper_write_impl (self=0x7bffb4361110, text=<optimized out>) at ./Modules/_io/textio.c:1765
#3  _io_TextIOWrapper_write (self=0x7bffb4361110, arg=0x5555569275c8 <_PyRuntime+96968>) at ./Modules/_io/clinic/textio.c.h:803
#4  0x0000555555ecb860 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:2499
#5  0x0000555555e96854 in _PyEval_EvalFrame (tstate=0x7e8ff7576210, frame=0x7e8ff7404328, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#6  _PyEval_Vector (tstate=0x7e8ff7576210, func=0x7bffb449b7d0, locals=0x0, args=0x7bff33fcf520, argcount=1, kwnames=0x0) at Python/ceval.c:2125
#7  0x0000555555ac3110 in _PyObject_VectorcallTstate (tstate=tstate@entry=0x7e8ff7576210, callable=callable@entry=0x7bffb449b7d0, args=0x7bff33fcf520, nargsf=nargsf@entry=1,
    kwnames=kwnames@entry=0x0) at ./Include/internal/pycore_call.h:136
#8  0x0000555555ac080a in method_vectorcall (method=0x7bff440d0070, args=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at Objects/classobject.c:74
#9  0x0000555555f582b3 in _PyObject_VectorcallTstate (tstate=0x7e8ff7576210, callable=0x7bff440d0070, args=0x7bff440e00e7, nargsf=17042036080667, kwnames=0x0)
    at ./Include/internal/pycore_call.h:136
#10 context_run (self=0x7bffb4d82770, args=0x7bfec37d47b0, nargs=1, kwnames=0x0) at Python/context.c:727
#11 0x0000555555e997da in _PyCallMethodDescriptorFastWithKeywords_StackRefSteal (callable=callable@entry=..., meth=0x555556740560 <PyContext_methods+160>, self=self@entry=0x7bffb4d82770,
    arguments=0x7e8ff74042f8, total_args=total_args@entry=2) at Python/ceval.c:913
#12 0x0000555555ec9cad in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:3852
#13 0x0000555555e96854 in _PyEval_EvalFrame (tstate=0x7e8ff7576210, frame=0x7e8ff7404220, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#14 _PyEval_Vector (tstate=0x7e8ff7576210, func=0x7bffb449b8b0, locals=0x0, args=0x7bff33fcd120, argcount=1, kwnames=0x0) at Python/ceval.c:2125
#15 0x0000555555ac3110 in _PyObject_VectorcallTstate (tstate=tstate@entry=0x7e8ff7576210, callable=callable@entry=0x7bffb449b8b0, args=0x7bff33fcd120, nargsf=nargsf@entry=1,
    kwnames=kwnames@entry=0x0) at ./Include/internal/pycore_call.h:136
#16 0x0000555555ac080a in method_vectorcall (method=0x7bffb457ff70, args=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at Objects/classobject.c:74
#17 0x0000555555ab955f in _PyVectorcall_Call (tstate=<optimized out>, func=<optimized out>, callable=<optimized out>, tuple=0x55555693a1f8 <_PyRuntime+173816>, kwargs=<optimized out>)
    at Objects/call.c:273
#18 0x00005555562b0b59 in thread_run (boot_raw=boot_raw@entry=0x7c6ff6fe83e0) at ./Modules/_threadmodule.c:387
#19 0x00005555560f8a26 in pythread_wrapper (arg=0x7c3ff70fa0a0) at Python/thread_pthread.h:234
#20 0x000055555589b34b in asan_thread_start(void*) ()
#21 0x00007ffff7ca3d64 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:448
#22 0x00007ffff7d373fc in __GI___clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

Abort backtrace

python: ./Modules/_io/textio.c:1765: PyObject *_io_TextIOWrapper_write_impl(textio *, PyObject *): Assertion `!PyList_CheckExact(self->pending_bytes)' failed.

Thread 65 "Thread-64 (prin" received signal SIGABRT, Aborted.
[Switching to Thread 0x7bff14a596c0 (LWP 1478715)]

#0  __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:100
#3  0x00007ffff7c45e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c28888 in __GI_abort () at ./stdlib/abort.c:77
#5  0x00007ffff7c287f0 in __assert_fail_base (fmt=<optimized out>, assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:118
#6  0x00007ffff7c3c19f in __assert_fail (assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:127
#7  0x000055555622f965 in _io_TextIOWrapper_write_impl (self=0x7bffb4361110, text=<optimized out>) at ./Modules/_io/textio.c:1765
#8  _io_TextIOWrapper_write (self=0x7bffb4361110, arg=0x5555569275c8 <_PyRuntime+96968>) at ./Modules/_io/clinic/textio.c.h:803
#9  0x0000555555ecb860 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:2499
#10 0x0000555555e96854 in _PyEval_EvalFrame (tstate=0x7e8ff741d210, frame=0x7e8ff718e328, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#11 _PyEval_Vector (tstate=0x7e8ff741d210, func=0x7bffb449b7d0, locals=0x0, args=0x7bff98169a20, argcount=1, kwnames=0x0) at Python/ceval.c:2125
#12 0x0000555555ac3110 in _PyObject_VectorcallTstate (tstate=tstate@entry=0x7e8ff741d210, callable=callable@entry=0x7bffb449b7d0, args=0x7bff98169a20, nargsf=nargsf@entry=1,
    kwnames=kwnames@entry=0x0) at ./Include/internal/pycore_call.h:136
#13 0x0000555555ac080a in method_vectorcall (method=0x7bffda0d0070, args=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at Objects/classobject.c:74
#14 0x0000555555f582b3 in _PyObject_VectorcallTstate (tstate=0x7e8ff741d210, callable=0x7bffda0d0070, args=0x7bff14a596c0, nargsf=22, kwnames=0x0) at ./Include/internal/pycore_call.h:136
#15 context_run (self=0x7bffb4d80850, args=0x7bff14a577b0, nargs=1, kwnames=0x0) at Python/context.c:727
#16 0x0000555555e997da in _PyCallMethodDescriptorFastWithKeywords_StackRefSteal (callable=callable@entry=..., meth=0x555556740560 <PyContext_methods+160>, self=self@entry=0x7bffb4d80850,
    arguments=0x7e8ff718e2f8, total_args=total_args@entry=2) at Python/ceval.c:913
#17 0x0000555555ec9cad in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:3852
#18 0x0000555555e96854 in _PyEval_EvalFrame (tstate=0x7e8ff741d210, frame=0x7e8ff718e220, throwflag=0) at ./Include/internal/pycore_ceval.h:118
#19 _PyEval_Vector (tstate=0x7e8ff741d210, func=0x7bffb449b8b0, locals=0x0, args=0x7bff98168120, argcount=1, kwnames=0x0) at Python/ceval.c:2125
#20 0x0000555555ac3110 in _PyObject_VectorcallTstate (tstate=tstate@entry=0x7e8ff741d210, callable=callable@entry=0x7bffb449b8b0, args=0x7bff98168120, nargsf=nargsf@entry=1,
    kwnames=kwnames@entry=0x0) at ./Include/internal/pycore_call.h:136
#21 0x0000555555ac080a in method_vectorcall (method=0x7bffb44e2a10, args=<optimized out>, nargsf=<optimized out>, kwnames=<optimized out>) at Objects/classobject.c:74
#22 0x0000555555ab955f in _PyVectorcall_Call (tstate=<optimized out>, func=<optimized out>, callable=<optimized out>, tuple=0x55555693a1f8 <_PyRuntime+173816>, kwargs=<optimized out>)
    at Objects/call.c:273
#23 0x00005555562b0b59 in thread_run (boot_raw=boot_raw@entry=0x7c6ff6fe61c0) at ./Modules/_threadmodule.c:387
#24 0x00005555560f8a26 in pythread_wrapper (arg=0x7c3ff70db460) at Python/thread_pthread.h:234
#25 0x000055555589b34b in asan_thread_start(void*) ()
#26 0x00007ffff7ca3d64 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:448
#27 0x00007ffff7d373fc in __GI___clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78)

Affected code:

cpython/Modules/_io/textio.c

Lines 1757 to 1769 in 66da7bf

else if (!PyList_CheckExact(self->pending_bytes)) {
PyObject *list = PyList_New(2);
if (list == NULL) {
Py_DECREF(b);
return NULL;
}
// Since Python 3.12, allocating GC object won't trigger GC and release
// GIL. See https://github.com/python/cpython/issues/97922
assert(!PyList_CheckExact(self->pending_bytes));
PyList_SET_ITEM(list, 0, self->pending_bytes);
PyList_SET_ITEM(list, 1, b);
self->pending_bytes = list;
}

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.0a6+ free-threading build (heads/main:b488f338cf0, Feb 12 2026, 17:28:06) [Clang 21.1.2 (2ubuntu6)]

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions