bpo-28994: PyErr_NormalizeException() no longer use C stack for recursion. (#2035)

MemoryError raised when normalizing a RecursionError raised during exception normalization now not always causes a fatal error.
This commit is contained in:
Serhiy Storchaka 2017-11-05 11:27:48 +02:00 committed by GitHub
parent 1b46131ae4
commit cf296537f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 35 additions and 41 deletions

View File

@ -228,20 +228,20 @@ PyErr_ExceptionMatches(PyObject *exc)
XXX: should PyErr_NormalizeException() also call XXX: should PyErr_NormalizeException() also call
PyException_SetTraceback() with the resulting value and tb? PyException_SetTraceback() with the resulting value and tb?
*/ */
static void void
PyErr_NormalizeExceptionEx(PyObject **exc, PyObject **val, PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
PyObject **tb, int recursion_depth)
{ {
PyObject *type = *exc; int recursion_depth = 0;
PyObject *value = *val; PyObject *type, *value, *initial_tb;
PyObject *inclass = NULL;
PyObject *initial_tb = NULL;
restart:
type = *exc;
if (type == NULL) { if (type == NULL) {
/* There was no exception, so nothing to do. */ /* There was no exception, so nothing to do. */
return; return;
} }
value = *val;
/* If PyErr_SetNone() was used, the value will have been actually /* If PyErr_SetNone() was used, the value will have been actually
set to NULL. set to NULL.
*/ */
@ -250,54 +250,52 @@ PyErr_NormalizeExceptionEx(PyObject **exc, PyObject **val,
Py_INCREF(value); Py_INCREF(value);
} }
if (PyExceptionInstance_Check(value))
inclass = PyExceptionInstance_Class(value);
/* Normalize the exception so that if the type is a class, the /* Normalize the exception so that if the type is a class, the
value will be an instance. value will be an instance.
*/ */
if (PyExceptionClass_Check(type)) { if (PyExceptionClass_Check(type)) {
int is_subclass; PyObject *inclass = NULL;
if (inclass) { int is_subclass = 0;
is_subclass = PyObject_IsSubclass(inclass, type);
if (is_subclass < 0)
goto finally;
}
else
is_subclass = 0;
/* if the value was not an instance, or is not an instance if (PyExceptionInstance_Check(value)) {
inclass = PyExceptionInstance_Class(value);
is_subclass = PyObject_IsSubclass(inclass, type);
if (is_subclass < 0) {
goto error;
}
}
/* If the value was not an instance, or is not an instance
whose class is (or is derived from) type, then use the whose class is (or is derived from) type, then use the
value as an argument to instantiation of the type value as an argument to instantiation of the type
class. class.
*/ */
if (!inclass || !is_subclass) { if (!is_subclass) {
PyObject *fixed_value; PyObject *fixed_value = _PyErr_CreateException(type, value);
fixed_value = _PyErr_CreateException(type, value);
if (fixed_value == NULL) { if (fixed_value == NULL) {
goto finally; goto error;
} }
Py_DECREF(value); Py_DECREF(value);
value = fixed_value; value = fixed_value;
} }
/* if the class of the instance doesn't exactly match the /* If the class of the instance doesn't exactly match the
class of the type, believe the instance class of the type, believe the instance.
*/ */
else if (inclass != type) { else if (inclass != type) {
Py_INCREF(inclass);
Py_DECREF(type); Py_DECREF(type);
type = inclass; type = inclass;
Py_INCREF(type);
} }
} }
*exc = type; *exc = type;
*val = value; *val = value;
return; return;
finally:
error:
Py_DECREF(type); Py_DECREF(type);
Py_DECREF(value); Py_DECREF(value);
if (recursion_depth + 1 == Py_NORMALIZE_RECURSION_LIMIT) { recursion_depth++;
if (recursion_depth == Py_NORMALIZE_RECURSION_LIMIT) {
PyErr_SetString(PyExc_RecursionError, "maximum recursion depth " PyErr_SetString(PyExc_RecursionError, "maximum recursion depth "
"exceeded while normalizing an exception"); "exceeded while normalizing an exception");
} }
@ -307,16 +305,18 @@ finally:
*/ */
initial_tb = *tb; initial_tb = *tb;
PyErr_Fetch(exc, val, tb); PyErr_Fetch(exc, val, tb);
assert(*exc != NULL);
if (initial_tb != NULL) { if (initial_tb != NULL) {
if (*tb == NULL) if (*tb == NULL)
*tb = initial_tb; *tb = initial_tb;
else else
Py_DECREF(initial_tb); Py_DECREF(initial_tb);
} }
/* Normalize recursively. /* Abort when Py_NORMALIZE_RECURSION_LIMIT has been exceeded, and the
* Abort when Py_NORMALIZE_RECURSION_LIMIT has been exceeded and the corresponding RecursionError could not be normalized, and the
* corresponding RecursionError could not be normalized.*/ MemoryError raised when normalize this RecursionError could not be
if (++recursion_depth > Py_NORMALIZE_RECURSION_LIMIT) { normalized. */
if (recursion_depth >= Py_NORMALIZE_RECURSION_LIMIT + 2) {
if (PyErr_GivenExceptionMatches(*exc, PyExc_MemoryError)) { if (PyErr_GivenExceptionMatches(*exc, PyExc_MemoryError)) {
Py_FatalError("Cannot recover from MemoryErrors " Py_FatalError("Cannot recover from MemoryErrors "
"while normalizing exceptions."); "while normalizing exceptions.");
@ -326,13 +326,7 @@ finally:
"of an exception."); "of an exception.");
} }
} }
PyErr_NormalizeExceptionEx(exc, val, tb, recursion_depth); goto restart;
}
void
PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb)
{
PyErr_NormalizeExceptionEx(exc, val, tb, 0);
} }