From 28521e917638da9dfb0782420a409af3b11ea22c Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 26 May 2025 17:28:24 -0700 Subject: [PATCH 1/2] Add defensive NULL checks in mro resolution Currently, there are a few places where tp_mro could theoretically become NULL, but do not in practice. We should defensively check for NULL values to ensure that any changes do not introduce a crash and that state invariants are upheld. --- Objects/typeobject.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index ee09289425b91a..5dff96631fbabf 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1652,6 +1652,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp) return res; } PyObject *new_mro = lookup_tp_mro(type); + assert(new_mro != NULL); PyObject *tuple; if (old_mro != NULL) { @@ -3274,6 +3275,7 @@ mro_implementation_unlocked(PyTypeObject *type) */ PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0)); PyObject *base_mro = lookup_tp_mro(base); + assert(base_mro != NULL); Py_ssize_t k = PyTuple_GET_SIZE(base_mro); PyObject *result = PyTuple_New(k + 1); if (result == NULL) { @@ -3308,9 +3310,12 @@ mro_implementation_unlocked(PyTypeObject *type) return NULL; } + PyObject *mro_to_merge; for (Py_ssize_t i = 0; i < n; i++) { PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i)); - to_merge[i] = lookup_tp_mro(base); + mro_to_merge = lookup_tp_mro(base); + assert(mro_to_merge != NULL); + to_merge[i] = mro_to_merge; } to_merge[n] = bases; @@ -8598,6 +8603,7 @@ type_ready_inherit(PyTypeObject *type) // Inherit slots PyObject *mro = lookup_tp_mro(type); + assert(mro != NULL); Py_ssize_t n = PyTuple_GET_SIZE(mro); for (Py_ssize_t i = 1; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(mro, i); From c305485a7f5c52a05e4ada291a60c4d8c0fc751c Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Tue, 27 May 2025 08:06:13 -0700 Subject: [PATCH 2/2] Rename mro_hierarchy to indicate it should be used with complete types --- Objects/typeobject.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 5dff96631fbabf..339aa3885edcd3 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1641,7 +1641,7 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name, update_callback callback, void *data); static int -mro_hierarchy(PyTypeObject *type, PyObject *temp) +mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp) { ASSERT_TYPE_LOCK_HELD(); @@ -1697,7 +1697,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp) Py_ssize_t n = PyList_GET_SIZE(subclasses); for (Py_ssize_t i = 0; i < n; i++) { PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i)); - res = mro_hierarchy(subclass, temp); + res = mro_hierarchy_for_complete_type(subclass, temp); if (res < 0) { break; } @@ -1779,7 +1779,7 @@ type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases) if (temp == NULL) { goto bail; } - if (mro_hierarchy(type, temp) < 0) { + if (mro_hierarchy_for_complete_type(type, temp) < 0) { goto undo; } Py_DECREF(temp);