Skip to content

Commit acda9f3

Browse files
bpo-46417: Fix race condition on setting type __bases__ (GH-30788) (GH-30789)
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 <[email protected]> Co-authored-by: Victor Stinner <[email protected]>
1 parent 6111d5d commit acda9f3

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
@@ -6384,24 +6384,29 @@ PyType_Ready(PyTypeObject *type)
63846384
static int
63856385
add_subclass(PyTypeObject *base, PyTypeObject *type)
63866386
{
6387-
int result = -1;
6388-
PyObject *dict, *key, *newobj;
6387+
PyObject *key = PyLong_FromVoidPtr((void *) type);
6388+
if (key == NULL)
6389+
return -1;
63896390

6390-
dict = base->tp_subclasses;
6391+
PyObject *ref = PyWeakref_NewRef((PyObject *)type, NULL);
6392+
if (ref == NULL) {
6393+
Py_DECREF(key);
6394+
return -1;
6395+
}
6396+
6397+
// Only get tp_subclasses after creating the key and value.
6398+
// PyWeakref_NewRef() can trigger a garbage collection which can execute
6399+
// arbitrary Python code and so modify base->tp_subclasses.
6400+
PyObject *dict = base->tp_subclasses;
63916401
if (dict == NULL) {
63926402
base->tp_subclasses = dict = PyDict_New();
63936403
if (dict == NULL)
63946404
return -1;
63956405
}
63966406
assert(PyDict_CheckExact(dict));
6397-
key = PyLong_FromVoidPtr((void *) type);
6398-
if (key == NULL)
6399-
return -1;
6400-
newobj = PyWeakref_NewRef((PyObject *)type, NULL);
6401-
if (newobj != NULL) {
6402-
result = PyDict_SetItem(dict, key, newobj);
6403-
Py_DECREF(newobj);
6404-
}
6407+
6408+
int result = PyDict_SetItem(dict, key, ref);
6409+
Py_DECREF(ref);
64056410
Py_DECREF(key);
64066411
return result;
64076412
}

0 commit comments

Comments
 (0)