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]