From 552c0218de3e173b370225e3c26c42a9dc3665e5 Mon Sep 17 00:00:00 2001 From: lipengyu Date: Fri, 12 Jun 2026 17:05:09 +0800 Subject: [PATCH 1/2] gh-151377: Fix races updating type slots and subclasses in free-threaded builds --- Objects/typeobject.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 388e8891c9c739..780a300c23b646 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -3943,7 +3943,7 @@ static void object_dealloc(PyObject *); static PyObject *object_new(PyTypeObject *, PyObject *, PyObject *); static int object_init(PyObject *, PyObject *, PyObject *); static int update_slot(PyTypeObject *, PyObject *, slot_update_t *update); -static void fixup_slot_dispatchers(PyTypeObject *); +static int fixup_slot_dispatchers(PyTypeObject *); static int type_new_set_names(PyTypeObject *); static int type_new_init_subclass(PyTypeObject *, PyObject *); static bool has_slotdef(PyObject *); @@ -4954,7 +4954,9 @@ type_new_impl(type_new_ctx *ctx) } // Put the proper slots in place - fixup_slot_dispatchers(type); + if (fixup_slot_dispatchers(type) < 0) { + goto error; + } if (!_PyDict_HasOnlyStringKeys(type->tp_dict)) { if (PyErr_WarnFormat( @@ -6841,7 +6843,13 @@ type_dealloc_common(PyTypeObject *type) PyObject *bases = lookup_tp_bases(type); if (bases != NULL) { PyObject *exc = PyErr_GetRaisedException(); +#ifdef Py_GIL_DISABLED + BEGIN_TYPE_LOCK(); +#endif remove_all_subclasses(type, bases); +#ifdef Py_GIL_DISABLED + END_TYPE_LOCK(); +#endif PyErr_SetRaisedException(exc); } } @@ -12117,13 +12125,33 @@ update_slot(PyTypeObject *type, PyObject *name, slot_update_t *queued_updates) /* Store the proper functions in the slot dispatches at class (type) definition time, based upon which operations the class overrides in its dict. */ -static void +static int fixup_slot_dispatchers(PyTypeObject *type) { assert(!PyErr_Occurred()); +#ifdef Py_GIL_DISABLED + slot_update_t queued_updates = {0}; + int res = 0; + BEGIN_TYPE_LOCK(); + for (pytype_slotdef *p = slotdefs; p->name; ) { + if (update_one_slot(type, p, &p, &queued_updates) < 0) { + res = -1; + break; + } + } + if (res == 0 && queued_updates.head != NULL) { + apply_type_slot_updates(&queued_updates); + ASSERT_TYPE_LOCK_HELD(); + } + END_TYPE_LOCK(); + slot_update_free_chunks(&queued_updates); + return res; +#else for (pytype_slotdef *p = slotdefs; p->name; ) { update_one_slot(type, p, &p, NULL); } + return 0; +#endif } #ifdef Py_GIL_DISABLED From 574d6a03851b6a112bbae0e6f2267280e2329c3b Mon Sep 17 00:00:00 2001 From: lipengyu Date: Fri, 12 Jun 2026 17:17:45 +0800 Subject: [PATCH 2/2] Create 2026-06-12-17-15-53.gh-issue-151377.bQSQUk.rst add NEWS entry --- .../2026-06-12-17-15-53.gh-issue-151377.bQSQUk.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-17-15-53.gh-issue-151377.bQSQUk.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-17-15-53.gh-issue-151377.bQSQUk.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-17-15-53.gh-issue-151377.bQSQUk.rst new file mode 100644 index 00000000000000..1e4fae5834ee10 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-06-12-17-15-53.gh-issue-151377.bQSQUk.rst @@ -0,0 +1,2 @@ +Fix races in free-threaded builds when updating type slots for newly created +classes and when removing entries from a base type's subclasses dictionary.