https://github.com/python/cpython/commit/b83f379a972c001864d3593cd64fc07e7c7f375f
commit: b83f379a972c001864d3593cd64fc07e7c7f375f
branch: main
author: Edward Xu <[email protected]>
committer: colesbury <[email protected]>
date: 2025-11-05T16:20:40-05:00
summary:

gh-133467: Fix typeobject `tp_base` race in free threading (gh-140549)

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-14-29-12.gh-issue-133467.A5d6TM.rst
M Lib/test/test_free_threading/test_type.py
M Objects/typeobject.c
M Tools/tsan/suppressions_free_threading.txt

diff --git a/Lib/test/test_free_threading/test_type.py 
b/Lib/test/test_free_threading/test_type.py
index 2d995751005d71..1255d842dbff48 100644
--- a/Lib/test/test_free_threading/test_type.py
+++ b/Lib/test/test_free_threading/test_type.py
@@ -141,6 +141,25 @@ def reader():
 
         self.run_one(writer, reader)
 
+    def test_bases_change(self):
+        class BaseA:
+            pass
+
+        class Derived(BaseA):
+            pass
+
+        def writer():
+            for _ in range(1000):
+                class BaseB:
+                    pass
+                Derived.__bases__ = (BaseB,)
+
+        def reader():
+            for _ in range(1000):
+                Derived.__base__
+
+        self.run_one(writer, reader)
+
     def run_one(self, writer_func, reader_func):
         barrier = threading.Barrier(NTHREADS)
 
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-14-29-12.gh-issue-133467.A5d6TM.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-14-29-12.gh-issue-133467.A5d6TM.rst
new file mode 100644
index 00000000000000..f69786866e9878
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-24-14-29-12.gh-issue-133467.A5d6TM.rst
@@ -0,0 +1 @@
+Fix race when updating :attr:`!type.__bases__` that could allow a read of 
:attr:`!type.__base__` to observe an inconsistent value on the free threaded 
build.
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 326f4add896bab..58228d6248522e 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -189,6 +189,8 @@ type_lock_allow_release(void)
 #define types_world_is_stopped() 1
 #define types_stop_world()
 #define types_start_world()
+#define type_lock_prevent_release()
+#define type_lock_allow_release()
 
 #endif
 
@@ -1920,8 +1922,12 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject 
*new_bases, PyTypeObject *b
     assert(old_bases != NULL);
     PyTypeObject *old_base = type->tp_base;
 
+    type_lock_prevent_release();
+    types_stop_world();
     set_tp_bases(type, Py_NewRef(new_bases), 0);
     type->tp_base = (PyTypeObject *)Py_NewRef(best_base);
+    types_start_world();
+    type_lock_allow_release();
 
     PyObject *temp = PyList_New(0);
     if (temp == NULL) {
@@ -1982,8 +1988,12 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject 
*new_bases, PyTypeObject *b
     if (lookup_tp_bases(type) == new_bases) {
         assert(type->tp_base == best_base);
 
+        type_lock_prevent_release();
+        types_stop_world();
         set_tp_bases(type, old_bases, 0);
         type->tp_base = old_base;
+        types_start_world();
+        type_lock_allow_release();
 
         Py_DECREF(new_bases);
         Py_DECREF(best_base);
diff --git a/Tools/tsan/suppressions_free_threading.txt 
b/Tools/tsan/suppressions_free_threading.txt
index 6bd31e8e6ecb9d..404c30157362aa 100644
--- a/Tools/tsan/suppressions_free_threading.txt
+++ b/Tools/tsan/suppressions_free_threading.txt
@@ -41,7 +41,3 @@ race:list_inplace_repeat_lock_held
 # PyObject_Realloc internally does memcpy which isn't atomic so can race
 # with non-locking reads. See #132070
 race:PyObject_Realloc
-
-# gh-133467.  Some of these could be hard to trigger.
-race_top:set_tp_bases
-race_top:type_set_bases_unlocked

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to