From 1db76394ea79030aa4ed5349c950f6c6da51450f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 21 Nov 2020 12:02:53 +0200 Subject: [PATCH] bpo-42412: Fix possible leaks and check arguments in PyType_FromModuleAndSpec() (GH-23410) * There were leaks if Py_tp_bases is used more than once or if some call is failed before setting tp_bases. * There was a crash if the bases argument or the Py_tp_bases slot is not a tuple. * The documentation was not accurate. --- Doc/c-api/type.rst | 6 ++++-- Objects/typeobject.c | 23 ++++++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 84c0a02046a..a822b671ac4 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -157,7 +157,8 @@ The following functions and structs are used to create If *bases* is a tuple, the created heap type contains all types contained in it as base types. - If *bases* is ``NULL``, the *Py_tp_base* slot is used instead. + If *bases* is ``NULL``, the *Py_tp_bases* slot is used instead. + If that also is ``NULL``, the *Py_tp_base* slot is used instead. If that also is ``NULL``, the new type derives from :class:`object`. The *module* argument can be used to record the module in which the new @@ -253,7 +254,8 @@ The following functions and structs are used to create * :c:member:`~PyBufferProcs.bf_getbuffer` * :c:member:`~PyBufferProcs.bf_releasebuffer` - Setting :c:data:`Py_tp_bases` may be problematic on some platforms. + Setting :c:data:`Py_tp_bases` or :c:data:`Py_tp_base` may be + problematic on some platforms. To avoid issues, use the *bases* argument of :py:func:`PyType_FromSpecWithBases` instead. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index fd018b8b032..9ebeeeb2c31 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -2977,26 +2977,40 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) base = slot->pfunc; else if (slot->slot == Py_tp_bases) { bases = slot->pfunc; - Py_INCREF(bases); } } - if (!bases) + if (!bases) { bases = PyTuple_Pack(1, base); - if (!bases) + if (!bases) + goto fail; + } + else if (!PyTuple_Check(bases)) { + PyErr_SetString(PyExc_SystemError, "Py_tp_bases is not a tuple"); goto fail; + } + else { + Py_INCREF(bases); + } } - else + else if (!PyTuple_Check(bases)) { + PyErr_SetString(PyExc_SystemError, "bases is not a tuple"); + goto fail; + } + else { Py_INCREF(bases); + } /* Calculate best base, and check that all bases are type objects */ base = best_base(bases); if (base == NULL) { + Py_DECREF(bases); goto fail; } if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { PyErr_Format(PyExc_TypeError, "type '%.100s' is not an acceptable base type", base->tp_name); + Py_DECREF(bases); goto fail; } @@ -3008,7 +3022,6 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases) type->tp_as_buffer = &res->as_buffer; /* Set tp_base and tp_bases */ type->tp_bases = bases; - bases = NULL; Py_INCREF(base); type->tp_base = base;