Skip to content

Commit a8edca6

Browse files
authored
gh-132835: Add defensive NULL checks to MRO resolution (GH-134763)
Currently, there are a few places where tp_mro could theoretically become NULL, but do not in practice. This commit adds defensive checks for NULL values to ensure that any changes do not introduce a crash and that state invariants are upheld. The assertions added in this commit are all instances where a NULL value would get passed to something not expecting a NULL, so it is better to catch an assertion failure than crash later on. There are a few cases where it is OK for the return of lookup_tp_mro to be NULL, such as when passed to is_subtype_with_mro, which handles this explicitly.
1 parent 02c1abf commit a8edca6

File tree

1 file changed

+10
-4
lines changed

1 file changed

+10
-4
lines changed

Objects/typeobject.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,7 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
17801780
// Compute tp_mro for this type and all of its subclasses. This
17811781
// is called after __bases__ is assigned to an existing type.
17821782
static int
1783-
mro_hierarchy(PyTypeObject *type, PyObject *temp)
1783+
mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp)
17841784
{
17851785
ASSERT_TYPE_LOCK_HELD();
17861786

@@ -1791,6 +1791,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
17911791
return res;
17921792
}
17931793
PyObject *new_mro = lookup_tp_mro(type);
1794+
assert(new_mro != NULL);
17941795

17951796
PyObject *tuple;
17961797
if (old_mro != NULL) {
@@ -1835,7 +1836,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
18351836
Py_ssize_t n = PyList_GET_SIZE(subclasses);
18361837
for (Py_ssize_t i = 0; i < n; i++) {
18371838
PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
1838-
res = mro_hierarchy(subclass, temp);
1839+
res = mro_hierarchy_for_complete_type(subclass, temp);
18391840
if (res < 0) {
18401841
break;
18411842
}
@@ -1926,7 +1927,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, PyTypeObject *b
19261927
if (temp == NULL) {
19271928
goto bail;
19281929
}
1929-
if (mro_hierarchy(type, temp) < 0) {
1930+
if (mro_hierarchy_for_complete_type(type, temp) < 0) {
19301931
goto undo;
19311932
}
19321933
Py_DECREF(temp);
@@ -3432,6 +3433,7 @@ mro_implementation_unlocked(PyTypeObject *type)
34323433
*/
34333434
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
34343435
PyObject *base_mro = lookup_tp_mro(base);
3436+
assert(base_mro != NULL);
34353437
Py_ssize_t k = PyTuple_GET_SIZE(base_mro);
34363438
PyObject *result = PyTuple_New(k + 1);
34373439
if (result == NULL) {
@@ -3466,9 +3468,12 @@ mro_implementation_unlocked(PyTypeObject *type)
34663468
return NULL;
34673469
}
34683470

3471+
PyObject *mro_to_merge;
34693472
for (Py_ssize_t i = 0; i < n; i++) {
34703473
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
3471-
to_merge[i] = lookup_tp_mro(base);
3474+
mro_to_merge = lookup_tp_mro(base);
3475+
assert(mro_to_merge != NULL);
3476+
to_merge[i] = mro_to_merge;
34723477
}
34733478
to_merge[n] = bases;
34743479

@@ -8998,6 +9003,7 @@ type_ready_inherit(PyTypeObject *type)
89989003

89999004
// Inherit slots
90009005
PyObject *mro = lookup_tp_mro(type);
9006+
assert(mro != NULL);
90019007
Py_ssize_t n = PyTuple_GET_SIZE(mro);
90029008
for (Py_ssize_t i = 1; i < n; i++) {
90039009
PyObject *b = PyTuple_GET_ITEM(mro, i);

0 commit comments

Comments
 (0)