diff options
author | Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> | 2020-05-22 21:35:22 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-22 21:35:22 (GMT) |
commit | 7f77ac463cff219e0c8afef2611cad5080cc9df1 (patch) | |
tree | 8d28a7f990479fcf34d2493553a8583b515d7e39 /Python | |
parent | a08b7c3bb0ef9da32400d23b13f78245cd7a9541 (diff) | |
download | cpython-7f77ac463cff219e0c8afef2611cad5080cc9df1.zip cpython-7f77ac463cff219e0c8afef2611cad5080cc9df1.tar.gz cpython-7f77ac463cff219e0c8afef2611cad5080cc9df1.tar.bz2 |
bpo-40696: Fix a hang that can arise after gen.throw() (GH-20287)
This updates _PyErr_ChainStackItem() to use _PyErr_SetObject()
instead of _PyErr_ChainExceptions(). This prevents a hang in
certain circumstances because _PyErr_SetObject() performs checks
to prevent cycles in the exception context chain while
_PyErr_ChainExceptions() doesn't.
(cherry picked from commit 7c30d12bd5359b0f66c4fbc98aa055398bcc8a7e)
Co-authored-by: Chris Jerdonek <chris.jerdonek@gmail.com>
Diffstat (limited to 'Python')
-rw-r--r-- | Python/errors.c | 62 |
1 files changed, 53 insertions, 9 deletions
diff --git a/Python/errors.c b/Python/errors.c index 3b42c11..70365aa 100644 --- a/Python/errors.c +++ b/Python/errors.c @@ -477,7 +477,9 @@ PyErr_SetExcInfo(PyObject *p_type, PyObject *p_value, PyObject *p_traceback) /* Like PyErr_Restore(), but if an exception is already set, set the context associated with it. - */ + + The caller is responsible for ensuring that this call won't create + any cycles in the exception context chain. */ void _PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb) { @@ -512,18 +514,60 @@ _PyErr_ChainExceptions(PyObject *exc, PyObject *val, PyObject *tb) } } +/* Set the currently set exception's context to the given exception. + + If the provided exc_info is NULL, then the current Python thread state's + exc_info will be used for the context instead. + + This function can only be called when _PyErr_Occurred() is true. + Also, this function won't create any cycles in the exception context + chain to the extent that _PyErr_SetObject ensures this. */ void -_PyErr_ChainStackItem(_PyErr_StackItem *exc_state) +_PyErr_ChainStackItem(_PyErr_StackItem *exc_info) { - if (exc_state->exc_type == NULL || exc_state->exc_type == Py_None) { + PyThreadState *tstate = _PyThreadState_GET(); + assert(_PyErr_Occurred(tstate)); + + int exc_info_given; + if (exc_info == NULL) { + exc_info_given = 0; + exc_info = tstate->exc_info; + } else { + exc_info_given = 1; + } + if (exc_info->exc_type == NULL || exc_info->exc_type == Py_None) { return; } - Py_INCREF(exc_state->exc_type); - Py_XINCREF(exc_state->exc_value); - Py_XINCREF(exc_state->exc_traceback); - _PyErr_ChainExceptions(exc_state->exc_type, - exc_state->exc_value, - exc_state->exc_traceback); + + _PyErr_StackItem *saved_exc_info; + if (exc_info_given) { + /* Temporarily set the thread state's exc_info since this is what + _PyErr_SetObject uses for implicit exception chaining. */ + saved_exc_info = tstate->exc_info; + tstate->exc_info = exc_info; + } + + PyObject *exc, *val, *tb; + _PyErr_Fetch(tstate, &exc, &val, &tb); + + PyObject *exc2, *val2, *tb2; + exc2 = exc_info->exc_type; + val2 = exc_info->exc_value; + tb2 = exc_info->exc_traceback; + _PyErr_NormalizeException(tstate, &exc2, &val2, &tb2); + if (tb2 != NULL) { + PyException_SetTraceback(val2, tb2); + } + + /* _PyErr_SetObject sets the context from PyThreadState. */ + _PyErr_SetObject(tstate, exc, val); + Py_DECREF(exc); // since _PyErr_Occurred was true + Py_XDECREF(val); + Py_XDECREF(tb); + + if (exc_info_given) { + tstate->exc_info = saved_exc_info; + } } static PyObject * |