class A(metaclass=M):
pass
+ def test_disappearing_custom_mro(self):
+ """
+ gh-92112: A custom mro() returning a result conflicting with
+ __bases__ and deleting itself caused a double free.
+ """
+ class B:
+ pass
+
+ class M(DebugHelperMeta):
+ def mro(cls):
+ del M.mro
+ return (B,)
+
+ with self.assertRaises(TypeError):
+ class A(metaclass=M):
+ pass
+
if __name__ == "__main__":
unittest.main()
Py_ssize_t i, n;
int custom = !Py_IS_TYPE(type, &PyType_Type);
int unbound;
- PyObject *mro_meth = NULL;
- PyObject *type_mro_meth = NULL;
if (!_PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
return;
if (custom) {
+ PyObject *mro_meth, *type_mro_meth;
mro_meth = lookup_maybe_method(
(PyObject *)type, &PyId_mro, &unbound);
- if (mro_meth == NULL)
+ if (mro_meth == NULL) {
goto clear;
+ }
type_mro_meth = lookup_maybe_method(
(PyObject *)&PyType_Type, &PyId_mro, &unbound);
- if (type_mro_meth == NULL)
+ if (type_mro_meth == NULL) {
+ Py_DECREF(mro_meth);
goto clear;
- if (mro_meth != type_mro_meth)
+ }
+ int custom_mro = (mro_meth != type_mro_meth);
+ Py_DECREF(mro_meth);
+ Py_DECREF(type_mro_meth);
+ if (custom_mro) {
goto clear;
- Py_XDECREF(mro_meth);
- Py_XDECREF(type_mro_meth);
+ }
}
n = PyTuple_GET_SIZE(bases);
for (i = 0; i < n; i++) {
}
return;
clear:
- Py_XDECREF(mro_meth);
- Py_XDECREF(type_mro_meth);
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
Py_TPFLAGS_VALID_VERSION_TAG);
}