aboutsummaryrefslogtreecommitdiff
path: root/Python/errors.c
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2021-08-10 10:37:25 +0100
committerGitHub <noreply@github.com>2021-08-10 11:37:25 +0200
commitd5c217475c4957a8084ac3f92ae012ece5edc7cb (patch)
tree25ebdc594d9cc6ca6e7ca2084b7680d8695db7f0 /Python/errors.c
parent6b37d0d5300813de31d66df1c77dad7e1027e4d8 (diff)
downloadcpython3-d5c217475c4957a8084ac3f92ae012ece5edc7cb.tar.gz
bpo-25782: avoid hang in PyErr_SetObject when current exception has a cycle in its context chain (GH-27626)
Co-authored-by: Dennis Sweeney 36520290+sweeneyde@users.noreply.github.com
Diffstat (limited to 'Python/errors.c')
-rw-r--r--Python/errors.c16
1 files changed, 15 insertions, 1 deletions
diff --git a/Python/errors.c b/Python/errors.c
index eeb84e8383..ae1cde690e 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -148,12 +148,16 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
value = fixed_value;
}
- /* Avoid reference cycles through the context chain.
+ /* Avoid creating new reference cycles through the
+ context chain, while taking care not to hang on
+ pre-existing ones.
This is O(chain length) but context chains are
usually very short. Sensitive readers may try
to inline the call to PyException_GetContext. */
if (exc_value != value) {
PyObject *o = exc_value, *context;
+ PyObject *slow_o = o; /* Floyd's cycle detection algo */
+ int slow_update_toggle = 0;
while ((context = PyException_GetContext(o))) {
Py_DECREF(context);
if (context == value) {
@@ -161,6 +165,16 @@ _PyErr_SetObject(PyThreadState *tstate, PyObject *exception, PyObject *value)
break;
}
o = context;
+ if (o == slow_o) {
+ /* pre-existing cycle - all exceptions on the
+ path were visited and checked. */
+ break;
+ }
+ if (slow_update_toggle) {
+ slow_o = PyException_GetContext(slow_o);
+ Py_DECREF(slow_o);
+ }
+ slow_update_toggle = !slow_update_toggle;
}
PyException_SetContext(value, exc_value);
}