gh-89373: _Py_Dealloc() checks tp_dealloc exception (#32357)

If Python is built in debug mode, _Py_Dealloc() now ensures that the
tp_dealloc function leaves the current exception unchanged.
This commit is contained in:
Victor Stinner 2022-04-21 23:04:01 +02:00 committed by GitHub
parent 8a4e519e78
commit 364ed94092
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 3 deletions

View file

@ -2354,11 +2354,45 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg,
void
_Py_Dealloc(PyObject *op)
{
destructor dealloc = Py_TYPE(op)->tp_dealloc;
PyTypeObject *type = Py_TYPE(op);
destructor dealloc = type->tp_dealloc;
#ifdef Py_DEBUG
PyThreadState *tstate = _PyThreadState_GET();
PyObject *old_exc_type = tstate->curexc_type;
// Keep the old exception type alive to prevent undefined behavior
// on (tstate->curexc_type != old_exc_type) below
Py_XINCREF(old_exc_type);
// Make sure that type->tp_name remains valid
Py_INCREF(type);
#endif
#ifdef Py_TRACE_REFS
_Py_ForgetReference(op);
#endif
(*dealloc)(op);
#ifdef Py_DEBUG
// gh-89373: The tp_dealloc function must leave the current exception
// unchanged.
if (tstate->curexc_type != old_exc_type) {
const char *err;
if (old_exc_type == NULL) {
err = "Deallocator of type '%s' raised an exception";
}
else if (tstate->curexc_type == NULL) {
err = "Deallocator of type '%s' cleared the current exception";
}
else {
// It can happen if dealloc() normalized the current exception.
// A deallocator function must not change the current exception,
// not even normalize it.
err = "Deallocator of type '%s' overrode the current exception";
}
_Py_FatalErrorFormat(__func__, err, type->tp_name);
}
Py_XDECREF(old_exc_type);
Py_DECREF(type);
#endif
}