Skip to content

Commit 9d547da

Browse files
[3.14] gh-132835: Add defensive NULL checks to MRO resolution (GH-134763) (GH-140436)
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. (cherry picked from commit a8edca6) Co-authored-by: Emma Smith <[email protected]>
1 parent a490d67 commit 9d547da

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
@@ -1658,7 +1658,7 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
16581658
update_callback callback, void *data);
16591659

16601660
static int
1661-
mro_hierarchy(PyTypeObject *type, PyObject *temp)
1661+
mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp)
16621662
{
16631663
ASSERT_TYPE_LOCK_HELD();
16641664

@@ -1669,6 +1669,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
16691669
return res;
16701670
}
16711671
PyObject *new_mro = lookup_tp_mro(type);
1672+
assert(new_mro != NULL);
16721673

16731674
PyObject *tuple;
16741675
if (old_mro != NULL) {
@@ -1713,7 +1714,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
17131714
Py_ssize_t n = PyList_GET_SIZE(subclasses);
17141715
for (Py_ssize_t i = 0; i < n; i++) {
17151716
PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
1716-
res = mro_hierarchy(subclass, temp);
1717+
res = mro_hierarchy_for_complete_type(subclass, temp);
17171718
if (res < 0) {
17181719
break;
17191720
}
@@ -1795,7 +1796,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases)
17951796
if (temp == NULL) {
17961797
goto bail;
17971798
}
1798-
if (mro_hierarchy(type, temp) < 0) {
1799+
if (mro_hierarchy_for_complete_type(type, temp) < 0) {
17991800
goto undo;
18001801
}
18011802
Py_DECREF(temp);
@@ -3291,6 +3292,7 @@ mro_implementation_unlocked(PyTypeObject *type)
32913292
*/
32923293
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
32933294
PyObject *base_mro = lookup_tp_mro(base);
3295+
assert(base_mro != NULL);
32943296
Py_ssize_t k = PyTuple_GET_SIZE(base_mro);
32953297
PyObject *result = PyTuple_New(k + 1);
32963298
if (result == NULL) {
@@ -3325,9 +3327,12 @@ mro_implementation_unlocked(PyTypeObject *type)
33253327
return NULL;
33263328
}
33273329

3330+
PyObject *mro_to_merge;
33283331
for (Py_ssize_t i = 0; i < n; i++) {
33293332
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
3330-
to_merge[i] = lookup_tp_mro(base);
3333+
mro_to_merge = lookup_tp_mro(base);
3334+
assert(mro_to_merge != NULL);
3335+
to_merge[i] = mro_to_merge;
33313336
}
33323337
to_merge[n] = bases;
33333338

@@ -8623,6 +8628,7 @@ type_ready_inherit(PyTypeObject *type)
86238628

86248629
// Inherit slots
86258630
PyObject *mro = lookup_tp_mro(type);
8631+
assert(mro != NULL);
86268632
Py_ssize_t n = PyTuple_GET_SIZE(mro);
86278633
for (Py_ssize_t i = 1; i < n; i++) {
86288634
PyObject *b = PyTuple_GET_ITEM(mro, i);

0 commit comments

Comments
 (0)