summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2020-05-22 21:35:22 (GMT)
committerGitHub <noreply@github.com>2020-05-22 21:35:22 (GMT)
commit7f77ac463cff219e0c8afef2611cad5080cc9df1 (patch)
tree8d28a7f990479fcf34d2493553a8583b515d7e39 /Python
parenta08b7c3bb0ef9da32400d23b13f78245cd7a9541 (diff)
downloadcpython-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.c62
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 *