close
Skip to content

gh-156204: Guard recursion in PyErr_GivenExceptionMatches - #156205

Open
BHUVANSH855 wants to merge 4 commits into
python:mainfrom
BHUVANSH855:fix-err-given-exception-matches-recursion
Open

gh-156204: Guard recursion in PyErr_GivenExceptionMatches#156205
BHUVANSH855 wants to merge 4 commits into
python:mainfrom
BHUVANSH855:fix-err-given-exception-matches-recursion

Conversation

@BHUVANSH855

@BHUVANSH855 BHUVANSH855 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Fixes issue gh-156204.

PyErr_GivenExceptionMatches in Python/errors.c recursively unpacks tuple targets without recursion checks, causing native C stack exhaustion and a SIGSEGV when given deeply nested exception tuples.

This change:

  • Guards recursion in PyErr_GivenExceptionMatches via Py_EnterRecursiveCall() and Py_LeaveRecursiveCall().
  • Adds a _testcapi.err_givenexceptionmatches() helper and a regression test in Lib/test/test_exceptions.py.
  • Adds a blurb NEWS entry.

Verified: with Python/errors.c reverted to the pre-fix version the new test segfaults; with the fix it passes.

Comment thread Lib/test/test_exceptions.py Outdated
ctypes = import_module('ctypes')
lib = ctypes.pythonapi
lib.PyErr_GivenExceptionMatches.argtypes = [ctypes.py_object, ctypes.py_object]
lib.PyErr_GivenExceptionMatches.restype = ctypes.c_int

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the test be written without ctypes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to _testcapi. Pure Python can't reach it: _PyEval_CheckExceptTypeValid() and get_matcher_type() both reject subtuples one level deep, so nested tuples only arrive via the C API.

Also found the old test asserted assertEqual(res, 0) - true with and without the fix. It now asserts RecursionError via support.run_with_limited_c_stack(); verified it segfaults on reverted errors.c.

One question: _Py_CheckRecursiveCall() replaces the current exception rather than chaining it, which matters for PyErr_ExceptionMatches() callers. Happy to use _Py_ReachedRecursionLimit() instead if you'd prefer.

Comment thread Misc/NEWS.d/next/Core_and_Builtins/2026-08-21-18-49-12.gh-issue-156204.ZusA7e.rst Outdated
@read-the-docs-community

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #34178190 | 📁 Comparing b6be6c5 against main (999a046)

  🔍 Preview build  

2 files changed
± c-api/exceptions.html
± whatsnew/changelog.html

Comment thread Doc/c-api/exceptions.rst Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants