Skip to content

Commit 30c2661

Browse files
[3.13] gh-132835: Add defensive NULL checks to MRO resolution (GH-134763) (GH-140437)
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 69230d1 commit 30c2661

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

15011501
static int
1502-
mro_hierarchy(PyTypeObject *type, PyObject *temp)
1502+
mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp)
15031503
{
15041504
ASSERT_TYPE_LOCK_HELD();
15051505

@@ -1510,6 +1510,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
15101510
return res;
15111511
}
15121512
PyObject *new_mro = lookup_tp_mro(type);
1513+
assert(new_mro != NULL);
15131514

15141515
PyObject *tuple;
15151516
if (old_mro != NULL) {
@@ -1554,7 +1555,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
15541555
Py_ssize_t n = PyList_GET_SIZE(subclasses);
15551556
for (Py_ssize_t i = 0; i < n; i++) {
15561557
PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
1557-
res = mro_hierarchy(subclass, temp);
1558+
res = mro_hierarchy_for_complete_type(subclass, temp);
15581559
if (res < 0) {
15591560
break;
15601561
}
@@ -1636,7 +1637,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, void *context)
16361637
if (temp == NULL) {
16371638
goto bail;
16381639
}
1639-
if (mro_hierarchy(type, temp) < 0) {
1640+
if (mro_hierarchy_for_complete_type(type, temp) < 0) {
16401641
goto undo;
16411642
}
16421643
Py_DECREF(temp);
@@ -2865,6 +2866,7 @@ mro_implementation_unlocked(PyTypeObject *type)
28652866
*/
28662867
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
28672868
PyObject *base_mro = lookup_tp_mro(base);
2869+
assert(base_mro != NULL);
28682870
Py_ssize_t k = PyTuple_GET_SIZE(base_mro);
28692871
PyObject *result = PyTuple_New(k + 1);
28702872
if (result == NULL) {
@@ -2899,9 +2901,12 @@ mro_implementation_unlocked(PyTypeObject *type)
28992901
return NULL;
29002902
}
29012903

2904+
PyObject *mro_to_merge;
29022905
for (Py_ssize_t i = 0; i < n; i++) {
29032906
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
2904-
to_merge[i] = lookup_tp_mro(base);
2907+
mro_to_merge = lookup_tp_mro(base);
2908+
assert(mro_to_merge != NULL);
2909+
to_merge[i] = mro_to_merge;
29052910
}
29062911
to_merge[n] = bases;
29072912

@@ -8014,6 +8019,7 @@ type_ready_inherit(PyTypeObject *type)
80148019

80158020
// Inherit slots
80168021
PyObject *mro = lookup_tp_mro(type);
8022+
assert(mro != NULL);
80178023
Py_ssize_t n = PyTuple_GET_SIZE(mro);
80188024
for (Py_ssize_t i = 1; i < n; i++) {
80198025
PyObject *b = PyTuple_GET_ITEM(mro, i);

0 commit comments

Comments
 (0)