From d5c217475c4957a8084ac3f92ae012ece5edc7cb Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 10 Aug 2021 10:37:25 +0100 Subject: 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 --- Python/errors.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'Python/errors.c') 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); } -- cgit v1.2.1