]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.15] gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threa...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 16 Jul 2026 10:23:07 +0000 (12:23 +0200)
committerGitHub <noreply@github.com>
Thu, 16 Jul 2026 10:23:07 +0000 (15:53 +0530)
gh-151644: Fix data race in sys.setdlopenflags/getdlopenflags under free-threading (gh-151768)

In free-threading builds, concurrent calls to sys.getdlopenflags() and
sys.setdlopenflags() race on interp->imports.dlopenflags.  Fix by
using FT_ATOMIC_LOAD_INT_RELAXED / FT_ATOMIC_STORE_INT_RELAXED in
_PyImport_GetDLOpenFlags and _PyImport_SetDLOpenFlags, consistent with
how analogous interpreter-state integer fields (lazy_imports_mode,
pystats_enabled) are protected.

Relaxed ordering is correct here: dlopenflags is a standalone config
integer with no ordering relationship to other memory.
(cherry picked from commit cde31ec135905472f1137dddcc8227af2430d89c)

Co-authored-by: Jiucheng(Oliver) <git.jiucheng@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Lib/test/test_free_threading/test_sys.py
Misc/NEWS.d/next/Core_and_Builtins/2026-06-18-00-00-00.gh-issue-151644.5cFffN.rst [new file with mode: 0644]
Python/import.c

index 37b53bd723fd767e6191c068bfbf813ccdb890b1..271fdd13c62b668eed7cacff356e366ff64b9bef 100644 (file)
@@ -4,6 +4,27 @@ from test.support import threading_helper
 
 
 class SysModuleTest(unittest.TestCase):
+    @unittest.skipUnless(hasattr(sys, "setdlopenflags"),
+                         "test needs sys.setdlopenflags()")
+    def test_dlopenflags_concurrent(self):
+        # gh-151644: getdlopenflags() and setdlopenflags() must be safe to
+        # call concurrently in free-threaded builds.
+        original = sys.getdlopenflags()
+        self.addCleanup(sys.setdlopenflags, original)
+
+        # Use a small set of known-valid flag values to avoid integer overflow.
+        flag_values = [1, 2, 256, 257]
+
+        def worker(worker_id):
+            for i in range(20_000):
+                if worker_id % 2 == 0:
+                    sys.getdlopenflags()
+                else:
+                    sys.setdlopenflags(flag_values[worker_id % len(flag_values)])
+
+        workers = [lambda i=i: worker(i) for i in range(6)]
+        threading_helper.run_concurrently(workers)
+
     def test_int_max_str_digits_thread(self):
         # gh-151218: Check that it's safe to call get_int_max_str_digits() and
         # set_int_max_str_digits() in parallel. Previously, this test triggered
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-18-00-00-00.gh-issue-151644.5cFffN.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-18-00-00-00.gh-issue-151644.5cFffN.rst
new file mode 100644 (file)
index 0000000..ff5058e
--- /dev/null
@@ -0,0 +1,4 @@
+Fix a data race in :func:`sys.setdlopenflags` and :func:`sys.getdlopenflags`
+when called concurrently in the free-threaded build. The underlying
+``_PyImport_GetDLOpenFlags`` and ``_PyImport_SetDLOpenFlags`` functions now
+use atomic load/store operations.
index 2c975295f9b5d3e28400bd41a4ee550bcdfd0c24..a47b2383e99d5f9c3f33b635fcdcf33df9245ad9 100644 (file)
@@ -911,13 +911,13 @@ _PyImport_SwapPackageContext(const char *newcontext)
 int
 _PyImport_GetDLOpenFlags(PyInterpreterState *interp)
 {
-    return DLOPENFLAGS(interp);
+    return FT_ATOMIC_LOAD_INT_RELAXED(DLOPENFLAGS(interp));
 }
 
 void
 _PyImport_SetDLOpenFlags(PyInterpreterState *interp, int new_val)
 {
-    DLOPENFLAGS(interp) = new_val;
+    FT_ATOMIC_STORE_INT_RELAXED(DLOPENFLAGS(interp), new_val);
 }
 #endif  // HAVE_DLOPEN