GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-155151: Check the recursion limit in CALL_EX_PY and CALL_KW_BOUND_METHOD by tekinertekin · Pull Request #155272 · python/cpython

Conversation

@tekinertekin

@tekinertekin tekinertekin commented Aug 6, 2026 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Fixes #155151.

The generic CALL_FUNCTION_EX and CALL_KW reach start_frame, which calls
_Py_EnterRecursivePy() and raises RecursionError before the callee runs.
Their specialized forms end in _PUSH_FRAME, which decrements
py_recursion_remaining without checking it. So once a call site had been
warmed up, the callee was entered and returned normally where the
unspecialized instruction raised — warming a call site changed whether the
target function executed.

CALL_KW_PY and CALL_BOUND_METHOD_EXACT_ARGS already guard their frame push
with _CHECK_RECURSION_REMAINING. This adds it to the two that were missing
it.

Where the check goes

_CHECK_RECURSION_REMAINING deopts, so it cannot follow a uop that has
already mutated the stack. Both placements sit where a deopt is already safe:

  • CALL_KW_BOUND_METHOD: after the existing flush that follows
    _EXPAND_METHOD_KW. This is the pattern CALL_BOUND_METHOD_EXACT_ARGS
    uses, where the same flush carries the comment "In case the following
    deopt"
    ahead of _CHECK_STACK_SPACE and _CHECK_RECURSION_REMAINING.
  • CALL_EX_PY: after _CHECK_IS_PY_CALLABLE_EX, which already EXIT_IFs at
    that point.

No new uop is introduced — _CHECK_RECURSION_REMAINING is already present in
executor_cases.c.h and optimizer_cases.c.h, and make regen-cases left
both files unchanged.

Verification

Built from main (free-threaded, --with-pydebug, TSAN) on macOS/arm64.

The reporter's four-case reproducer, before and after:

case opcode before after
call-ex CALL_FUNCTION_EX (control) RecursionError, 0 executions unchanged
call-ex CALL_EX_PY (specialized) returned 42, 1 execution RecursionError, 0 executions
call-kw-bound-method CALL_KW (control) RecursionError, 0 executions unchanged
call-kw-bound-method CALL_KW_BOUND_METHOD (specialized) returned 43, 1 execution RecursionError, 0 executions

The call sites still specialize afterwards — dis(..., adaptive=True) still
reports CALL_EX_PY and CALL_KW_BOUND_METHOD, so this adds the check rather
than suppressing the specialization.

Two regression tests were added next to test_recursion_check_for_general_calls
in test_opcache.py. They warm the call site, assert it specialized, then make
the call from the deepest live frame at the recursion limit and assert the
target never ran. Both fail on unpatched main with AssertionError: RecursionError not raised. The targets bump the counter with plain bytecode
rather than through another call, so reaching them cannot be masked by a second
recursion check.

test_opcache passes (84 tests), including under -R 3:3. test_call,
test_dis, test_generated_cases, test_monitoring, test_optimizer,
test_sys_settrace and test_capi pass. Tools/patchcheck is clean.

I did not build with --enable-experimental-jit locally, so the JIT job here
will be the first check of the tier 2 side.

AI tools were used on this PR: Claude Code (Opus) reproduced the issue, wrote
the patch and the tests, and ran the verification above. I reviewed the change,
understand it and can explain it, and I take responsibility for it.

  • Issue: CALL_EX_PY and CALL_KW_BOUND_METHOD can bypass RecursionError after specialization #155151

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@tekinertekin
pythongh-155151: Check the recursion limit in CALL_EX_PY and CALL_KW_…
70a3de4
…BOUND_METHOD

The generic CALL_FUNCTION_EX and CALL_KW opcodes reach start_frame, which
calls _Py_EnterRecursivePy() and raises RecursionError before the callee
runs. Their specialized forms end in _PUSH_FRAME, which decrements
py_recursion_remaining without checking it, so once a call site had been
warmed up the callee was entered and returned normally where the
unspecialized instruction raised. Warming a call site should not change
whether the target function executes.

CALL_KW_PY and CALL_BOUND_METHOD_EXACT_ARGS already guard their frame push
with _CHECK_RECURSION_REMAINING; add it to these two as well. It deopts, so
it goes where a deopt is already safe: after the existing flush in
CALL_KW_BOUND_METHOD, following the pattern of
CALL_BOUND_METHOD_EXACT_ARGS, and after _CHECK_IS_PY_CALLABLE_EX in
CALL_EX_PY, which already exits at that point.
@tekinertekin
tekinertekin requested a review from markshannon as a code owner August 6, 2026 10:51
@bedevere-app bedevere-app Bot added the awaiting review label Aug 6, 2026
@bedevere-app bedevere-app Bot mentioned this pull request Aug 6, 2026
CALL_EX_PY and CALL_KW_BOUND_METHOD can bypass RecursionError after specialization #155151
Open
@picnixz

picnixz commented Aug 6, 2026

Copy link
Copy Markdown
Member

The description of this PR and #155269 is almost identical, and thus even the description was probably generated entirely by an agent. In particular, I consider this as an excessive use of agents and will reject this PR especially since it touches the core interpreter.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz picnixz closed this Aug 6, 2026
@picnixz picnixz mentioned this pull request Aug 9, 2026
gh-155151: Check the recursion limit in CALL_EX_PY and CALL_KW_BOUND_METHOD #155405
Closed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

CALL_EX_PY and CALL_KW_BOUND_METHOD can bypass RecursionError after specialization

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@tekinertekin @picnixz
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.