Skip to content

Commit 141b181

Browse files
committed
gh-156204: report recursion overflow as unraisable
1 parent 6765ff2 commit 141b181

4 files changed

Lines changed: 18 additions & 10 deletions

File tree

‎Lib/test/test_exceptions.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2827,8 +2827,10 @@ def test_given_exception_matches_deeply_nested_tuple(self):
28272827
for _ in range(500_000):
28282828
tup = (tup,)
28292829

2830-
with self.assertRaises(RecursionError):
2831-
_testcapi.err_givenexceptionmatches(TypeError(), tup)
2830+
with support.catch_unraisable_exception() as cm:
2831+
self.assertFalse(
2832+
_testcapi.err_givenexceptionmatches(ValueError(), tup))
2833+
self.assertIsInstance(cm.unraisable.exc_value, RecursionError)
28322834

28332835

28342836
class PEP626Tests(unittest.TestCase):
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Fix unhandled recursion error in :c:func:`PyErr_GivenExceptionMatches` when
2-
evaluating deeply nested exception tuples, preventing crashes caused by
3-
stack exhaustion.
1+
Fix a crash in :c:func:`PyErr_GivenExceptionMatches` when evaluating deeply
2+
nested exception tuples. The recursion is now bounded, and exceeding the
3+
limit is reported as an unraisable exception.

‎Modules/_testcapi/exceptions.c‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ _testcapi_err_givenexceptionmatches_impl(PyObject *module, PyObject *err,
7070
{
7171
assert(!PyErr_Occurred());
7272
int res = PyErr_GivenExceptionMatches(err, exc);
73-
if (res == 0 && PyErr_Occurred()) {
74-
return NULL;
75-
}
73+
assert(!PyErr_Occurred());
7674
return PyBool_FromLong(res);
7775
}
7876

‎Python/errors.c‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "Python.h"
55
#include "pycore_audit.h" // _PySys_Audit()
66
#include "pycore_call.h" // _PyObject_CallNoArgs()
7+
#include "pycore_ceval.h" // _Py_ReachedRecursionLimitWithMargin()
78
#include "pycore_fileutils.h" // _PyFile_Flush
89
#include "pycore_initconfig.h" // _PyStatus_ERR()
910
#include "pycore_pyerrors.h" // _PyErr_Format()
@@ -337,7 +338,15 @@ PyErr_GivenExceptionMatches(PyObject *err, PyObject *exc)
337338
return 0;
338339
}
339340
if (PyTuple_Check(exc)) {
340-
if (Py_EnterRecursiveCall(" in PyErr_GivenExceptionMatches")) {
341+
PyThreadState *tstate = _PyThreadState_GET();
342+
if (_Py_ReachedRecursionLimitWithMargin(tstate, 2)) {
343+
PyObject *exc_value = _PyErr_GetRaisedException(tstate);
344+
_PyErr_SetString(tstate, PyExc_RecursionError,
345+
"maximum recursion depth exceeded while "
346+
"checking exception tuple");
347+
PyErr_FormatUnraisable("Exception ignored while "
348+
"checking exception tuple");
349+
_PyErr_SetRaisedException(tstate, exc_value);
341350
return 0;
342351
}
343352
int res = 0;
@@ -349,7 +358,6 @@ PyErr_GivenExceptionMatches(PyObject *err, PyObject *exc)
349358
break;
350359
}
351360
}
352-
Py_LeaveRecursiveCall();
353361
return res;
354362
}
355363
/* err might be an instance, so check its class. */

0 commit comments

Comments
 (0)