Skip to content

Commit f1c6ae3

Browse files
authored
bpo-46417: Fix race condition on setting type __bases__ (GH-30788)
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.
1 parent c8a5366 commit f1c6ae3

File tree

2 files changed

+21
-11
lines changed

2 files changed

+21
-11
lines changed
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

+16-11
Original file line numberDiff line numberDiff line change
@@ -6503,24 +6503,29 @@ PyType_Ready(PyTypeObject *type)
65036503
static int
65046504
add_subclass(PyTypeObject *base, PyTypeObject *type)
65056505
{
6506-
int result = -1;
6507-
PyObject *dict, *key, *newobj;
6506+
PyObject *key = PyLong_FromVoidPtr((void *) type);
6507+
if (key == NULL)
6508+
return -1;
65086509

6509-
dict = base->tp_subclasses;
6510+
PyObject *ref = PyWeakref_NewRef((PyObject *)type, NULL);
6511+
if (ref == NULL) {
6512+
Py_DECREF(key);
6513+
return -1;
6514+
}
6515+
6516+
// Only get tp_subclasses after creating the key and value.
6517+
// PyWeakref_NewRef() can trigger a garbage collection which can execute
6518+
// arbitrary Python code and so modify base->tp_subclasses.
6519+
PyObject *dict = base->tp_subclasses;
65106520
if (dict == NULL) {
65116521
base->tp_subclasses = dict = PyDict_New();
65126522
if (dict == NULL)
65136523
return -1;
65146524
}
65156525
assert(PyDict_CheckExact(dict));
6516-
key = PyLong_FromVoidPtr((void *) type);
6517-
if (key == NULL)
6518-
return -1;
6519-
newobj = PyWeakref_NewRef((PyObject *)type, NULL);
6520-
if (newobj != NULL) {
6521-
result = PyDict_SetItem(dict, key, newobj);
6522-
Py_DECREF(newobj);
6523-
}
6526+
6527+
int result = PyDict_SetItem(dict, key, ref);
6528+
Py_DECREF(ref);
65246529
Py_DECREF(key);
65256530
return result;
65266531
}

0 commit comments

Comments
 (0)