]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-120198: Fix race condition when editing __class__ with an audit hook active (GH...
authorKen Jin <kenjin@python.org>
Tue, 11 Jun 2024 19:10:23 +0000 (03:10 +0800)
committerGitHub <noreply@github.com>
Tue, 11 Jun 2024 19:10:23 +0000 (20:10 +0100)
Lib/test/test_free_threading/test_type.py
Lib/test/test_super.py
Misc/NEWS.d/next/Core and Builtins/2024-06-10-15-07-16.gh-issue-120198.WW_pjO.rst [new file with mode: 0644]
Objects/typeobject.c

index 6eead198deed46283c32acdf4cb914e2166de256..786336fa0cddce7763aea3a7f20a0bcf93862e0d 100644 (file)
@@ -1,3 +1,4 @@
+import threading
 import unittest
 
 from concurrent.futures import ThreadPoolExecutor
index 256b416caaa584637872a59688fe869f0dfcbca5..3ffbe03f0c2f1108818d42c7b3d04f185092d5ef 100644 (file)
@@ -1,9 +1,10 @@
 """Unit tests for zero-argument super() & related machinery."""
 
 import textwrap
+import threading
 import unittest
 from unittest.mock import patch
-from test.support import import_helper
+from test.support import import_helper, threading_helper
 
 
 ADAPTIVE_WARMUP_DELAY = 2
@@ -505,6 +506,38 @@ class TestSuper(unittest.TestCase):
         for _ in range(ADAPTIVE_WARMUP_DELAY):
             C.some(C)
 
+    @threading_helper.requires_working_threading()
+    def test___class___modification_multithreaded(self):
+        """ Note: this test isn't actually testing anything on its own.
+        It requires a sys audithook to be set to crash on older Python.
+        This should be the case anyways as our test suite sets
+        an audit hook.
+        """
+        class Foo:
+            pass
+
+        class Bar:
+            pass
+
+        thing = Foo()
+        def work():
+            foo = thing
+            for _ in range(5000):
+                foo.__class__ = Bar
+                type(foo)
+                foo.__class__ = Foo
+                type(foo)
+
+
+        threads = []
+        for _ in range(6):
+            thread = threading.Thread(target=work)
+            thread.start()
+            threads.append(thread)
+
+        for thread in threads:
+            thread.join()
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-10-15-07-16.gh-issue-120198.WW_pjO.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-10-15-07-16.gh-issue-120198.WW_pjO.rst
new file mode 100644 (file)
index 0000000..8dc8aec
--- /dev/null
@@ -0,0 +1 @@
+Fix a crash when multiple threads read and write to the same ``__class__`` of an object concurrently.
index cd16bebd1e1cb84d03c343f3d074c394b3312a6d..070e3d2f7bf2b472055a2985cbef5f84317a3503 100644 (file)
@@ -6522,7 +6522,6 @@ differs:
 static int
 object_set_class(PyObject *self, PyObject *value, void *closure)
 {
-    PyTypeObject *oldto = Py_TYPE(self);
 
     if (value == NULL) {
         PyErr_SetString(PyExc_TypeError,
@@ -6542,6 +6541,8 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
         return -1;
     }
 
+    PyTypeObject *oldto = Py_TYPE(self);
+
     /* In versions of CPython prior to 3.5, the code in
        compatible_for_assignment was not set up to correctly check for memory
        layout / slot / etc. compatibility for non-HEAPTYPE classes, so we just