Skip to content

Crash when concurrently writing with print and concurrently modifying sys.stdout #130163

Open
@devdanzin

Description

Crash report

What happened?

Playing with the code from #130148, I stumbled on a segfault in a free-threaded debug build with the following (seems to trigger faster/more consistently when pasted in the REPL):

from contextlib import redirect_stdout
from io import StringIO
from threading import Thread
import time


def test_redirect():
    text = StringIO()
    with redirect_stdout(text):
        print("hello1", file=text)
        time.sleep(0.1)
        print("hello2", file=text)
    print(text.getvalue())
    assert text.getvalue() == "hello1\nhello2\n"


for x in range(100):
    Thread(target=test_redirect, args=()).start()

I didn't have time to check whether JIT, debug or no-gil are strictly necessary for this to crash. JIT is not needed for this to crash.

Cause

The problem is that in the print() implementation, _PySys_GetAttr returns a borrowed reference.

cpython/Python/bltinmodule.c

Lines 2173 to 2175 in 655fc8a

if (file == Py_None) {
PyThreadState *tstate = _PyThreadState_GET();
file = _PySys_GetAttr(tstate, &_Py_ID(stdout));

So if sys.stdout changes concurrently with the print(), the program may crash because file will point to a deallocated Python object.

This affects the GIL-enabled build as well. See this reproducer: https://gist.github.com/colesbury/c48f50e95d5d68e24814a56e2664e587

Suggested fix

Introduce a _PySys_GetAttrRef that returns a new reference instead of a borrowed reference. Use that instead.

We should audit all the uses of _PySys_GetAttr and PySys_GetObject. I expect most of them will need to be replaced with functions that return new references, but it doesn't all have to be in a single PR.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

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

Python 3.14.0a5+ experimental free-threading build (heads/main:359c7dde3bb, Feb 15 2025, 17:54:11) [GCC 11.4.0]

Linked PRs

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)topic-C-APItype-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