Repository navigation
Data race in _PyImport_SetDLOpenFlags when sys.setdlopenflags() is called concurrently #151644
Copy link
Copy link
Closed
Closed
Copy link
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
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.
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
Linked PRs
- gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threading #151768
- [3.14] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threading (gh-151768) #153789
- [3.15] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threading (gh-151768) #153790
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
interpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Activity
pythongh-151644: Fix data race in sys.setdlopenflags/getdlopenflags u…
zangjiucheng commentedon Jun 20, 2026
Hey Everyone,
Similar to gh-151646, this is a data race in free-threading builds between
concurrent
sys.getdlopenflags()(reader) andsys.setdlopenflags()(writer)on
interp->imports.dlopenflags.Fix: use
FT_ATOMIC_LOAD_INT_RELAXED/FT_ATOMIC_STORE_INT_RELAXEDin_PyImport_GetDLOpenFlags/_PyImport_SetDLOpenFlags.We chose atomics over
PyMutex(used in gh-151646) becausedlopenflagsis asingle
intfield — each access is exactly one load or one store. A mutex wouldbe overkill. Relaxed ordering is sufficient: this is a standalone config integer
with no ordering relationship to other memory, consistent with how
lazy_imports_modeandpystats_enabledare protected in the same codebase.Verified on Linux x86-64 free-threading build: 12 threads × 20,000 concurrent
get/set iterations exit cleanly.
Let me know if some suggestion :)
Misc/NEWS.d: fix news entry filename format for pythongh-151644
gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under f…
nascheme commentedon Jul 3, 2026
Thank you for the PR.
[3.15] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags …
[3.14] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags …