https://github.com/python/cpython/commit/85dabb9c1592e6b1990d7a8d29c9ec872aaf750e
commit: 85dabb9c1592e6b1990d7a8d29c9ec872aaf750e
branch: 3.14
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
date: 2025-10-07T17:36:56Z
summary:
[3.14] gh-137238: Fix data race in `_Py_slot_tp_getattr_hook` (gh-137240)
(#137416)
Replacing the slot isn't thread-safe if the GIL is disabled. Don't
require that the slot has been replaced when specializing.
(cherry picked from commit 485b16b4f7b28cefdfb524c2869d473078e349bf)
files:
M Objects/typeobject.c
M Python/specialize.c
M Tools/tsan/suppressions_free_threading.txt
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index ab6ad1fe8b5f91..06b4998507ce9f 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -10278,7 +10278,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
diff --git a/Python/specialize.c b/Python/specialize.c
index 545098eb51d422..dd00f1cb6b0024 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -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)) {
diff --git a/Tools/tsan/suppressions_free_threading.txt
b/Tools/tsan/suppressions_free_threading.txt
index 3230f969436c82..580757ddd75033 100644
--- a/Tools/tsan/suppressions_free_threading.txt
+++ b/Tools/tsan/suppressions_free_threading.txt
@@ -50,7 +50,6 @@ race:PyObject_Realloc
# gh-133467. Some of these could be hard to trigger.
race_top:update_one_slot
-race_top:_Py_slot_tp_getattr_hook
race_top:slot_tp_descr_get
race_top:type_set_name
race_top:set_tp_bases
_______________________________________________
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]