Skip to content

Commit df43d6a

Browse files
committed
gh-156204: Guard recursion in PyErr_GivenExceptionMatches
1 parent 999a046 commit df43d6a

3 files changed

Lines changed: 30 additions & 6 deletions

File tree

‎Lib/test/test_exceptions.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2778,6 +2778,22 @@ def test_except_star_invalid_exception_type(self):
27782778
except (ValueError, 42):
27792779
pass
27802780

2781+
def test_given_exception_matches_deeply_nested_tuple(self):
2782+
import ctypes
2783+
lib = ctypes.pythonapi
2784+
lib.PyErr_GivenExceptionMatches.argtypes = [ctypes.py_object, ctypes.py_object]
2785+
lib.PyErr_GivenExceptionMatches.restype = ctypes.c_int
2786+
2787+
tup = (1, ValueError)
2788+
for _ in range(50_000):
2789+
tup = (1, tup)
2790+
2791+
# PyErr_GivenExceptionMatches should handle deep recursion safely without SIGSEGV
2792+
res = lib.PyErr_GivenExceptionMatches(TypeError(), tup)
2793+
self.assertEqual(res, 0)
2794+
if lib.PyErr_Occurred():
2795+
lib.PyErr_Clear()
2796+
27812797

27822798
class PEP626Tests(unittest.TestCase):
27832799

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix unhandled recursion in :c:func:`PyErr_GivenExceptionMatches` when
2+
evaluating deeply nested exception tuples, preventing crashes caused by
3+
stack exhaustion.

‎Python/errors.c‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -335,16 +335,21 @@ PyErr_GivenExceptionMatches(PyObject *err, PyObject *exc)
335335
return 0;
336336
}
337337
if (PyTuple_Check(exc)) {
338+
if (Py_EnterRecursiveCall(" in PyErr_GivenExceptionMatches")) {
339+
return 0;
340+
}
338341
Py_ssize_t i, n;
339-
n = PyTuple_Size(exc);
342+
n = PyTuple_GET_SIZE(exc);
340343
for (i = 0; i < n; i++) {
341344
/* Test recursively */
342-
if (PyErr_GivenExceptionMatches(
343-
err, PyTuple_GET_ITEM(exc, i)))
344-
{
345-
return 1;
346-
}
345+
if (PyErr_GivenExceptionMatches(
346+
err, PyTuple_GET_ITEM(exc, i)))
347+
{
348+
Py_LeaveRecursiveCall();
349+
return 1;
350+
}
347351
}
352+
Py_LeaveRecursiveCall();
348353
return 0;
349354
}
350355
/* err might be an instance, so check its class. */

0 commit comments

Comments
 (0)