Skip to content

Data race in _PyImport_SetDLOpenFlags when sys.setdlopenflags() is called concurrently #151644

@Naserume

Description

@Naserume

Bug report

Bug description:

In the free-threaded build, calling sys.setdlopenflags() concurrently from multiple threads (or concurrently with sys.getdlopenflags()) data-races on the per-interpreter dlopenflags field: _PyImport_GetDLOpenFlags / _PyImport_SetDLOpenFlags read and write (interp)->imports.dlopenflags with no atomics or lock.

cpython/Python/import.c

Lines 908 to 918 in 9e863fa

int
_PyImport_GetDLOpenFlags(PyInterpreterState *interp)
{
return DLOPENFLAGS(interp);
}
void
_PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val)
{
DLOPENFLAGS(interp) = new_val;
}

sys.setdlopenflags() / sys.getdlopenflags() expose these directly, so concurrent calls produce a non-atomic write/write (and read/write) race on the field.

Reproducer:

import sys
from threading import Thread, Barrier

N = 12
barrier = Barrier(N)

def worker():
    barrier.wait()
    for _ in range(20000):
        d = sys.getdlopenflags()
        sys.setdlopenflags(d)

if __name__ == "__main__":
    threads = [Thread(target=worker) for _ in range(N)]
    for t in threads: t.start()
    for t in threads: t.join()

TSAN Report:

==================
WARNING: ThreadSanitizer: data race (pid=2296999)
  Read of size 4 at 0x555555e3f648 by thread T1:
    #0 _PyImport_GetDLOpenFlags /cpython/Python/import.c:911:12 
    #1 sys_getdlopenflags_impl /cpython/./Python/sysmodule.c:1838:13 
    #2 sys_getdlopenflags /cpython/./Python/clinic/sysmodule.c.h:823:12 
    #3 cfunction_vectorcall_NOARGS /cpython/Objects/methodobject.c:508:24 
    #4 _PyObject_VectorcallTstate /cpython/./Include/internal/pycore_call.h:144:11 
    #5 PyObject_Vectorcall /cpython/Objects/call.c:327:12 
    #6 _Py_VectorCallInstrumentation_StackRefSteal /cpython/Python/ceval.c:768:11 
    #7 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:1846:35 
    ...

  Previous write of size 4 at 0x555555e3f648 by thread T12:
    #0 _PyImport_SetDLOpenFlags /cpython/Python/import.c:917:25 
    #1 sys_setdlopenflags_impl /cpython/./Python/sysmodule.c:1819:5 
    #2 sys_setdlopenflags /cpython/./Python/clinic/sysmodule.c.h:796:20 
    #3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2612:35 
    ...

  Location is global '_PyRuntime' of size 405824 at 0x555555e16c80 

SUMMARY: ThreadSanitizer: data race /cpython/Python/import.c:911:12 in _PyImport_GetDLOpenFlags
==================
==================
WARNING: ThreadSanitizer: data race (pid=2296999)
  Write of size 4 at 0x555555e3f648 by thread T12:
    #0 _PyImport_SetDLOpenFlags /cpython/Python/import.c:917:25 
    #1 sys_setdlopenflags_impl /cpython/./Python/sysmodule.c:1819:5 
    #2 sys_setdlopenflags /cpython/./Python/clinic/sysmodule.c.h:796:20 
    #3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2612:35 
    ...

  Previous write of size 4 at 0x555555e3f648 by thread T9:
    #0 _PyImport_SetDLOpenFlags /cpython/Python/import.c:917:25 
    #1 sys_setdlopenflags_impl /cpython/./Python/sysmodule.c:1819:5 
    #2 sys_setdlopenflags /cpython/./Python/clinic/sysmodule.c.h:796:20 
    #3 _PyEval_EvalFrameDefault /cpython/Python/generated_cases.c.h:2612:35 
    ...

  Location is global '_PyRuntime' of size 405824 at 0x555555e16c80 

SUMMARY: ThreadSanitizer: data race /cpython/Python/import.c:917:25 in _PyImport_SetDLOpenFlags
==================

CPython versions tested on:

3.16

Operating systems tested on:

Linux

Metadata

Metadata

Assignees

No one assigned

    Labels

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions