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.
This commit is contained in:
Serhiy Storchaka 2020-11-21 12:02:53 +02:00 committed by GitHub
parent 01a202ab6b
commit 1db76394ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 22 additions and 7 deletions

View File

@ -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 If *bases* is a tuple, the created heap type contains all types contained
in it as base types. 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`. 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 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_getbuffer`
* :c:member:`~PyBufferProcs.bf_releasebuffer` * :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 To avoid issues, use the *bases* argument of
:py:func:`PyType_FromSpecWithBases` instead. :py:func:`PyType_FromSpecWithBases` instead.

View File

@ -2977,26 +2977,40 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
base = slot->pfunc; base = slot->pfunc;
else if (slot->slot == Py_tp_bases) { else if (slot->slot == Py_tp_bases) {
bases = slot->pfunc; bases = slot->pfunc;
Py_INCREF(bases);
} }
} }
if (!bases) if (!bases) {
bases = PyTuple_Pack(1, base); 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; 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); Py_INCREF(bases);
}
/* Calculate best base, and check that all bases are type objects */ /* Calculate best base, and check that all bases are type objects */
base = best_base(bases); base = best_base(bases);
if (base == NULL) { if (base == NULL) {
Py_DECREF(bases);
goto fail; goto fail;
} }
if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"type '%.100s' is not an acceptable base type", "type '%.100s' is not an acceptable base type",
base->tp_name); base->tp_name);
Py_DECREF(bases);
goto fail; goto fail;
} }
@ -3008,7 +3022,6 @@ PyType_FromModuleAndSpec(PyObject *module, PyType_Spec *spec, PyObject *bases)
type->tp_as_buffer = &res->as_buffer; type->tp_as_buffer = &res->as_buffer;
/* Set tp_base and tp_bases */ /* Set tp_base and tp_bases */
type->tp_bases = bases; type->tp_bases = bases;
bases = NULL;
Py_INCREF(base); Py_INCREF(base);
type->tp_base = base; type->tp_base = base;