diff options
author | Irit Katriel <1055913+iritkatriel@users.noreply.github.com> | 2021-08-10 10:37:25 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-10 11:37:25 +0200 |
commit | d5c217475c4957a8084ac3f92ae012ece5edc7cb (patch) | |
tree | 25ebdc594d9cc6ca6e7ca2084b7680d8695db7f0 /Python/errors.c | |
parent | 6b37d0d5300813de31d66df1c77dad7e1027e4d8 (diff) | |
download | cpython-git-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.c | 16 |
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); } |