From 4dcbd8fda3dde2b7805c3e191a92e621ecd6030c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 9 Jun 2017 22:56:04 +0300 Subject: [PATCH 1/2] bpo-28994: PyErr_NormalizeException() no longer recursive. --- Python/errors.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/Python/errors.c b/Python/errors.c index 3785e6981c6498..6816ec12a99ca9 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -226,17 +226,18 @@ PyErr_ExceptionMatches(PyObject *exc) void PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) { - PyObject *type = *exc; - PyObject *value = *val; - PyObject *inclass = NULL; - PyObject *initial_tb = NULL; - PyThreadState *tstate = NULL; + int recursion_depth = 0; + PyObject *type, *value, *initial_tb; + PyThreadState *tstate; +restart: + type = *exc; if (type == NULL) { /* There was no exception, so nothing to do. */ return; } + value = *val; /* If PyErr_SetNone() was used, the value will have been actually set to NULL. */ @@ -245,14 +246,18 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) Py_INCREF(value); } - if (PyExceptionInstance_Check(value)) - inclass = PyExceptionInstance_Class(value); - /* Normalize the exception so that if the type is a class, the value will be an instance. */ if (PyExceptionClass_Check(type)) { + PyObject *inclass; int is_subclass; + + if (PyExceptionInstance_Check(value)) + inclass = PyExceptionInstance_Class(value); + else + inclass = NULL; + if (inclass) { is_subclass = PyObject_IsSubclass(inclass, type); if (is_subclass < 0) @@ -266,7 +271,7 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) value as an argument to instantiation of the type class. */ - if (!inclass || !is_subclass) { + if (!is_subclass) { PyObject *fixed_value; fixed_value = _PyErr_CreateException(type, value); @@ -289,6 +294,7 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) *exc = type; *val = value; return; + finally: Py_DECREF(type); Py_DECREF(value); @@ -298,6 +304,7 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) */ initial_tb = *tb; PyErr_Fetch(exc, val, tb); + assert(*exc != NULL); if (initial_tb != NULL) { if (*tb == NULL) *tb = initial_tb; @@ -306,18 +313,17 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) } /* normalize recursively */ tstate = PyThreadState_GET(); - if (++tstate->recursion_depth > Py_GetRecursionLimit()) { - --tstate->recursion_depth; + if (++recursion_depth > Py_GetRecursionLimit() - tstate->recursion_depth) { /* throw away the old exception and use the recursion error instead */ Py_INCREF(PyExc_RecursionError); Py_SETREF(*exc, PyExc_RecursionError); Py_INCREF(PyExc_RecursionErrorInst); - Py_SETREF(*val, PyExc_RecursionErrorInst); + Py_XSETREF(*val, PyExc_RecursionErrorInst); /* just keeping the old traceback */ return; } - PyErr_NormalizeException(exc, val, tb); - --tstate->recursion_depth; + /* eliminate tail recursion */ + goto restart; } From 9a6bcbd5a2a96e309ca67695ce20e80125a8120a Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 4 Nov 2017 16:53:18 +0200 Subject: [PATCH 2/2] Clean up the code and take a chance MemoryError raised by normalizing RecursionError to be normalized. --- Python/errors.c | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/Python/errors.c b/Python/errors.c index a54c38bc75efac..15e6ba05714337 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -234,7 +234,7 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) int recursion_depth = 0; PyObject *type, *value, *initial_tb; -restart: + restart: type = *exc; if (type == NULL) { /* There was no exception, so nothing to do. */ @@ -254,55 +254,48 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) value will be an instance. */ if (PyExceptionClass_Check(type)) { - PyObject *inclass; - int is_subclass; + PyObject *inclass = NULL; + int is_subclass = 0; - if (PyExceptionInstance_Check(value)) + if (PyExceptionInstance_Check(value)) { inclass = PyExceptionInstance_Class(value); - else - inclass = NULL; - - if (inclass) { is_subclass = PyObject_IsSubclass(inclass, type); - if (is_subclass < 0) - goto finally; + if (is_subclass < 0) { + goto error; + } } - else - is_subclass = 0; - /* if the value was not an instance, or is not an instance + /* If the value was not an instance, or is not an instance whose class is (or is derived from) type, then use the value as an argument to instantiation of the type class. */ if (!is_subclass) { - PyObject *fixed_value; - - fixed_value = _PyErr_CreateException(type, value); + PyObject *fixed_value = _PyErr_CreateException(type, value); if (fixed_value == NULL) { - goto finally; + goto error; } - Py_DECREF(value); value = fixed_value; } - /* if the class of the instance doesn't exactly match the - class of the type, believe the instance + /* If the class of the instance doesn't exactly match the + class of the type, believe the instance. */ else if (inclass != type) { + Py_INCREF(inclass); Py_DECREF(type); type = inclass; - Py_INCREF(type); } } *exc = type; *val = value; return; -finally: + error: Py_DECREF(type); 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 " "exceeded while normalizing an exception"); } @@ -319,10 +312,11 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) else Py_DECREF(initial_tb); } - /* Normalize recursively. - * Abort when Py_NORMALIZE_RECURSION_LIMIT has been exceeded and the - * corresponding RecursionError could not be normalized.*/ - if (++recursion_depth > Py_NORMALIZE_RECURSION_LIMIT) { + /* Abort when Py_NORMALIZE_RECURSION_LIMIT has been exceeded, and the + corresponding RecursionError could not be normalized, and the + MemoryError raised when normalize this RecursionError could not be + normalized. */ + if (recursion_depth >= Py_NORMALIZE_RECURSION_LIMIT + 2) { if (PyErr_GivenExceptionMatches(*exc, PyExc_MemoryError)) { Py_FatalError("Cannot recover from MemoryErrors " "while normalizing exceptions."); @@ -332,7 +326,6 @@ PyErr_NormalizeException(PyObject **exc, PyObject **val, PyObject **tb) "of an exception."); } } - /* eliminate tail recursion */ goto restart; }