Skip to content

gh-130279: Fix missing exception handlers for jumps in loops - #159460

Merged
iritkatriel merged 13 commits into
python:mainfrom
LH-and-FPGA:gh-130279-backward-jump-handler
Oct 19, 2026
Merged

iritkatriel merged 13 commits into
python:mainfrom
LH-and-FPGA:gh-130279-backward-jump-handler

Conversation

@LH-and-FPGA

Copy link
Copy Markdown
Contributor

When a loop body ends with an if whose false branch jumps back to the loop head, the backward jump ended up outside the exception table ranges of the enclosing try/with. An exception raised when that JUMP_BACKWARD checks the eval breaker (e.g. KeyboardInterrupt, or an async exception set via PyThreadState_SetAsyncExc) then skipped the except/finally blocks, and __exit__ was not called for with blocks.

def scan(items):
    try:
        for x in items:
            if x < 0:
                raise ValueError(x)
    except KeyboardInterrupt:
        return "handled"

Two places in Python/flowgraph.c created jump instructions without an exception handler after label_exception_targets() had already run:

  • jump_thread() replaced the original jump with a new one, dropping its i_except.
  • normalize_jumps_in_block() added NOT_TAKEN + JUMP for backward conditional jumps without copying the handler.

Both now copy the handler of the original jump.

@python-cla-bot

python-cla-bot Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

Comment thread Python/flowgraph.c Outdated
@@ -586,6 +586,13 @@ normalize_jumps_in_block(cfg_builder *g, basicblock *b) {
basicblock_addop(backwards_jump, NOT_TAKEN, 0, last->i_loc));
RETURN_IF_ERROR(
basicblock_add_jump(backwards_jump, JUMP, target, last->i_loc));

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.

We could add an except arg to basicblock_addop and basicblock_add_jump (pass NULL if there is none).

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.

Thanks, I'll add an except on those places which replaces the post-hoc fixups.

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.

While doing this I noticed that the forward-branch NOT_TAKEN in normalize_jumps_in_block() also gets no handler, which leaves a gap in the exception table. It could matter when a sys.monitoring BRANCH_LEFT callback raises. Should I fix it here, or in a separate issue?

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.

Fix it here.

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.

Sure

Comment thread Python/flowgraph.c Outdated
Comment on lines +587 to +590
/* The new instructions are covered by the same exception handler as
* the conditional jump, since the backward jump can raise when it
* checks the eval breaker.
*/

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.

I don't think this comment is necessary. We copy i_loc and i_except, it makes sense.

@iritkatriel

Copy link
Copy Markdown
Member

Does this need to be backported?

@LH-and-FPGA

Copy link
Copy Markdown
Contributor Author

Does this need to be backported?

Do you mean fix this for 3.13 3.14 and 3.15?

@iritkatriel

Copy link
Copy Markdown
Member

Yes.

@LH-and-FPGA

Copy link
Copy Markdown
Contributor Author

Yes.

3.14 and 3.15 have the same code, so I think it should go to both. 3.13 and 3.12 has the backward jump issue as well, but it's security-only now. so how should I backport in general? Thanks

@iritkatriel iritkatriel added needs backport to 3.14 bugs and security fixes needs backport to 3.15 bugs and security fixes labels Oct 18, 2026
Comment thread Lib/test/test_compile.py Outdated
@LH-and-FPGA

LH-and-FPGA commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor Author

Does this need to be backported?

I found this https://devguide.python.org/getting-started/git-boot-camp/#backporting-merged-changes, so basically I don't need to do anything I think?

Comment thread Misc/NEWS.d/next/Core_and_Builtins/2026-10-15-12-36-09.gh-issue-130279.N9N-sw.rst Outdated
Comment thread Python/flowgraph.c Outdated
basicblock_add_jump(
bb, opcode, target->i_target, target->i_loc));
bb, opcode, target->i_target, target->i_loc,
inst->i_except));

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.

Why are we taking location from target but handler from inst?

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.

they should be the same in practice. jumps don't change the exception stack, and at this stage the NOPs left by POP_BLOCK haven't been removed yet, so threading never crosses a try boundary ( and I checked by compiling the stdlib with a debug check, and there were no mismatches). But the new jump stands in for target, so does that mean taking both from target is more consistent? Thanks.

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.

Let's take it from target then, so it doesn't look strange.

I'm seeing some failures in test_dis on the builedbot.

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.

The test_dis failures are indeed caused by this PR. The expected disassembly for dis_traceback, with and async with captured the old behaviour i think. but the NOT_TAKEN in the except clause wasn't covered by the exception table. With the forward NOT_TAKEN now getting the handler, that gap is gone. The adjacent exception table entries merge and there are fewer labels, so I think the expected output just needs updating. I missed this locally because of stale .pyc files in pycache. Should I push a fix to the expected output?

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.

Yes, if the changes are expected and not due to bugs then update the tests.

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.

Sure,

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.

Updated.

@iritkatriel

Copy link
Copy Markdown
Member

Does this need to be backported?

I found this https://devguide.python.org/getting-started/git-boot-camp/#backporting-merged-changes, so basically I don't need to do anything I think?

I put the labels for back porting. If it goes smoothly you don't do anything. If there are conflicts you'll need to resolve them (create the back port manually off the 3.14/3.15 branches).

@iritkatriel

Copy link
Copy Markdown
Member

Is this your first PR? If so then add your name to the contributors file.

@iritkatriel
iritkatriel merged commit 036d309 into python:main Oct 19, 2026
56 checks passed
@miss-islington-app

Copy link
Copy Markdown

Thanks @LH-and-FPGA for the PR, and @iritkatriel for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14, 3.15.
🐍🍒⛏🤖

@miss-islington-app

Copy link
Copy Markdown

Sorry, @LH-and-FPGA and @iritkatriel, I could not cleanly backport this to 3.14 due to a conflict.

Please backport manually with cherry_picker, see the devguide for more information.

cherry_picker 036d309cf542986a39eeba5ca805b2ec7619731d 3.14

@bedevere-app

bedevere-app Bot commented Oct 19, 2026

Copy link
Copy Markdown

GH-159698 is a backport of this pull request to the 3.15 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.15 bugs and security fixes label Oct 19, 2026
@iritkatriel

Copy link
Copy Markdown
Member

Thank you @LH-and-FPGA!

@iritkatriel

Copy link
Copy Markdown
Member

The 3.15 back port worked but 3.14 needs help.

@LH-and-FPGA

Copy link
Copy Markdown
Contributor Author

Thanks for the review, @iritkatriel! I’ll look into backporting this to 3.14.

iritkatriel pushed a commit that referenced this pull request Oct 19, 2026
…H-159460) (#159698)

gh-130279: Fix missing exception handlers for jumps in loops (GH-159460)
(cherry picked from commit 036d309)

Co-authored-by: Han <180024497+LH-and-FPGA@users.noreply.github.com>
@bedevere-app

bedevere-app Bot commented Oct 19, 2026

Copy link
Copy Markdown

GH-159703 is a backport of this pull request to the 3.14 branch.

@bedevere-app bedevere-app Bot removed the needs backport to 3.14 bugs and security fixes label Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants