Skip to content

Commit f1796f2

Browse files
bpo-46417: Fix race condition on setting type __bases__ (GH-30788) (GH-30790)
Fix a race condition on setting a type __bases__ attribute: the internal function add_subclass() now gets the PyTypeObject.tp_subclasses member after calling PyWeakref_NewRef() which can trigger a garbage collection which can indirectly modify PyTypeObject.tp_subclasses. (cherry picked from commit f1c6ae3) Co-authored-by: Victor Stinner <vstinner@python.org> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 6ed874f commit f1796f2

File tree

2 files changed

+21
-11
lines changed

2 files changed

+21
-11
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fix a race condition on setting a type ``__bases__`` attribute: the internal
2+
function ``add_subclass()`` now gets the ``PyTypeObject.tp_subclasses``
3+
member after calling :c:func:`PyWeakref_NewRef` which can trigger a garbage
4+
collection which can indirectly modify ``PyTypeObject.tp_subclasses``. Patch
5+
by Victor Stinner.

Objects/typeobject.c

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5622,24 +5622,29 @@ PyType_Ready(PyTypeObject *type)
56225622
static int
56235623
add_subclass(PyTypeObject *base, PyTypeObject *type)
56245624
{
5625-
int result = -1;
5626-
PyObject *dict, *key, *newobj;
5625+
PyObject *key = PyLong_FromVoidPtr((void *) type);
5626+
if (key == NULL)
5627+
return -1;
56275628

5628-
dict = base->tp_subclasses;
5629+
PyObject *ref = PyWeakref_NewRef((PyObject *)type, NULL);
5630+
if (ref == NULL) {
5631+
Py_DECREF(key);
5632+
return -1;
5633+
}
5634+
5635+
// Only get tp_subclasses after creating the key and value.
5636+
// PyWeakref_NewRef() can trigger a garbage collection which can execute
5637+
// arbitrary Python code and so modify base->tp_subclasses.
5638+
PyObject *dict = base->tp_subclasses;
56295639
if (dict == NULL) {
56305640
base->tp_subclasses = dict = PyDict_New();
56315641
if (dict == NULL)
56325642
return -1;
56335643
}
56345644
assert(PyDict_CheckExact(dict));
5635-
key = PyLong_FromVoidPtr((void *) type);
5636-
if (key == NULL)
5637-
return -1;
5638-
newobj = PyWeakref_NewRef((PyObject *)type, NULL);
5639-
if (newobj != NULL) {
5640-
result = PyDict_SetItem(dict, key, newobj);
5641-
Py_DECREF(newobj);
5642-
}
5645+
5646+
int result = PyDict_SetItem(dict, key, ref);
5647+
Py_DECREF(ref);
56435648
Py_DECREF(key);
56445649
return result;
56455650
}

0 commit comments

Comments
 (0)