Skip to content

Data race in PyThread_tss_delete and _PyThreadState_Attach #130091

@colesbury

Description

@colesbury

Bug report

There are a few data races during Python shutdown. This report involves thread-local variables using Py_tss_t.

It can be reproduced sometimes by running test_threading.test_import_from_another_thread with TSAN repeatedly.

WARNING: ThreadSanitizer: data race (pid=19107)
  Write of size 4 at 0x55f728ab09f8 by main thread:
    #0 PyThread_tss_delete /home/runner/work/cpython/cpython/Python/thread_pthread.h:974:26 (python+0x61de2c) (BuildId: 4fc02961f60c9c3851edd7ecf2a252cdf887e381)
    #1 tstate_tss_fini /home/runner/work/cpython/cpython/Python/pystate.c:[13](https://github.com/python/cpython/actions/runs/13313503854/job/37181762988#step:14:14)9:5 (python+0x5e82bf) (BuildId: 4fc02961f60c9c3851edd7ecf2a252cdf887e381)
    #2 _PyRuntimeState_Fini /home/runner/work/cpython/cpython/Python/pystate.c:489:9 (python+0x5e82bf)
    #3 _PyRuntime_Finalize /home/runner/work/cpython/cpython/Python/pylifecycle.c:[14](https://github.com/python/cpython/actions/runs/13313503854/job/37181762988#step:14:15)1:5 (python+0x5c186d) (BuildId: 4fc02961f60c9c3851edd7ecf2a252cdf887e381)
    #4 _Py_Finalize /home/runner/work/cpython/cpython/Python/pylifecycle.c:2197:5 (python+0x5c186d)
...

  Previous read of size 4 at 0x55f728ab09f8 by thread T1:
    #0 PyThread_tss_is_created /home/runner/work/cpython/cpython/Python/thread.c:208:17 (python+0x61e43e) (BuildId: 4fc02961f60c9c3851edd7ecf2a252cdf887e381)
    #1 tstate_tss_initialized /home/runner/work/cpython/cpython/Python/pystate.c:125:12 (python+0x5edc6d) (BuildId: 4fc02961f60c9c3851edd7ecf2a252cdf887e381)
    #2 tstate_tss_get /home/runner/work/cpython/cpython/Python/pystate.c:145:5 (python+0x5edc6d)
    #3 tstate_activate /home/runner/work/cpython/cpython/Python/pystate.c:2010:5 (python+0x5edc6d)
    #4 _PyThreadState_Attach /home/runner/work/cpython/cpython/Python/pystate.c:2093:9 (python+0x5edc6d)
    #5 _Py_HandlePending /home/runner/work/cpython/cpython/Python/ceval_gil.c:1263:9 (python+0x5729e8) (BuildId: 4fc02961f60c9c3851edd7ecf2a252cdf887e381)
    #6 _PyEval_EvalFrameDefault /home/runner/work/cpython/cpython/Python/generated_cases.c.h:1431:31 (python+0x4c21a7) (BuildId: 4fc02961f60c9c3851edd7ecf2a252cdf887e381)
...

The problem is that we are reading gilstate_tss_get((tstate->interp->runtime)) in an assertion, but that thread local variable is getting deleted concurrently by the thread calling _PyRuntime_Finalize.

cpython/Python/pystate.c

Lines 2010 to 2011 in 07f5e33

assert(!tstate->_status.bound_gilstate ||
tstate == gilstate_tss_get((tstate->interp->runtime)));

cpython/Python/pystate.c

Lines 2088 to 2097 in 07f5e33

while (1) {
_PyEval_AcquireLock(tstate);
// XXX assert(tstate_is_alive(tstate));
current_fast_set(&_PyRuntime, tstate);
tstate_activate(tstate);
if (!tstate_try_attach(tstate)) {
tstate_wait_attach(tstate);
}

The default build doesn't have this problem because the thread T1 hangs earlier in the _PyEval_AcquireLock() call. In the free threaded build, the hang will occur later in tstate_wait_attach().

See also:

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.13bugs and security fixes3.14bugs and security fixestopic-free-threadingtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions