]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-137238: Fix data race in `_Py_slot_tp_getattr_hook` (gh-137240)
authorSam Gross <colesbury@gmail.com>
Tue, 5 Aug 2025 13:32:22 +0000 (09:32 -0400)
committerGitHub <noreply@github.com>
Tue, 5 Aug 2025 13:32:22 +0000 (09:32 -0400)
Replacing the slot isn't thread-safe if the GIL is disabled. Don't
require that the slot has been replaced when specializing.

Objects/typeobject.c
Python/specialize.c
Tools/tsan/suppressions_free_threading.txt

index 6e67b6e01cb8b8fda285885292d08b9321a384b0..14bc5a4bc49f848f6463d3cd00b6ba316eae4284 100644 (file)
@@ -10585,7 +10585,10 @@ _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name)
     getattr = _PyType_LookupRef(tp, &_Py_ID(__getattr__));
     if (getattr == NULL) {
         /* No __getattr__ hook: use a simpler dispatcher */
+#ifndef Py_GIL_DISABLED
+        // Replacing the slot is only thread-safe if there is a GIL.
         tp->tp_getattro = _Py_slot_tp_getattro;
+#endif
         return _Py_slot_tp_getattro(self, name);
     }
     /* speed hack: we could use lookup_maybe, but that would resolve the
index fe8d04cf3442f1840483d88570f149dca355f334..38df5741f32520950ad492b7fb277e511e603367 100644 (file)
@@ -935,8 +935,7 @@ analyze_descriptor_load(PyTypeObject *type, PyObject *name, PyObject **descr, un
         PyObject *getattr = _PyType_Lookup(type, &_Py_ID(__getattr__));
         has_getattr = getattr != NULL;
         if (has_custom_getattribute) {
-            if (getattro_slot == _Py_slot_tp_getattro &&
-                !has_getattr &&
+            if (!has_getattr &&
                 Py_IS_TYPE(getattribute, &PyFunction_Type)) {
                 *descr = getattribute;
                 *tp_version = ga_version;
@@ -1259,12 +1258,6 @@ do_specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject*
             return -1;
         case GETATTRIBUTE_IS_PYTHON_FUNCTION:
         {
-            #ifndef Py_GIL_DISABLED
-            // In free-threaded builds it's possible for tp_getattro to change
-            // after the call to analyze_descriptor. That is fine: the version
-            // guard will fail.
-            assert(type->tp_getattro == _Py_slot_tp_getattro);
-            #endif
             assert(Py_IS_TYPE(descr, &PyFunction_Type));
             _PyLoadMethodCache *lm_cache = (_PyLoadMethodCache *)(instr + 1);
             if (!function_check_args(descr, 2, LOAD_ATTR)) {
index 52d7c25a5bb37afe7a41915c98875044cc74b356..6bd31e8e6ecb9d68fca90ca28108ce4f18ba872c 100644 (file)
@@ -43,6 +43,5 @@ race:list_inplace_repeat_lock_held
 race:PyObject_Realloc
 
 # gh-133467.  Some of these could be hard to trigger.
-race_top:_Py_slot_tp_getattr_hook
 race_top:set_tp_bases
 race_top:type_set_bases_unlocked