Repository navigation
gh-130279: Fix missing exception handlers for jumps in loops - #159460
Conversation
| @@ -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)); | |||
There was a problem hiding this comment.
We could add an except arg to basicblock_addop and basicblock_add_jump (pass NULL if there is none).
There was a problem hiding this comment.
Thanks, I'll add an except on those places which replaces the post-hoc fixups.
There was a problem hiding this comment.
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?
| /* 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. | ||
| */ |
There was a problem hiding this comment.
I don't think this comment is necessary. We copy i_loc and i_except, it makes sense.
|
Does this need to be backported? |
Do you mean fix this for 3.13 3.14 and 3.15? |
|
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 |
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? |
| basicblock_add_jump( | ||
| bb, opcode, target->i_target, target->i_loc)); | ||
| bb, opcode, target->i_target, target->i_loc, | ||
| inst->i_except)); |
There was a problem hiding this comment.
Why are we taking location from target but handler from inst?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Let's take it from target then, so it doesn't look strange.
I'm seeing some failures in test_dis on the builedbot.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Yes, if the changes are expected and not due to bugs then update the tests.
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). |
|
Is this your first PR? If so then add your name to the contributors file. |
|
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. |
|
Sorry, @LH-and-FPGA and @iritkatriel, I could not cleanly backport this to Please backport manually with cherry_picker, see the devguide for more information. |
|
GH-159698 is a backport of this pull request to the 3.15 branch. |
|
Thank you @LH-and-FPGA! |
|
The 3.15 back port worked but 3.14 needs help. |
|
Thanks for the review, @iritkatriel! I’ll look into backporting this to 3.14. |
|
GH-159703 is a backport of this pull request to the 3.14 branch. |
When a loop body ends with an
ifwhose false branch jumps back to the loop head, the backward jump ended up outside the exception table ranges of the enclosingtry/with. An exception raised when thatJUMP_BACKWARDchecks the eval breaker (e.g.KeyboardInterrupt, or an async exception set viaPyThreadState_SetAsyncExc) then skipped theexcept/finallyblocks, and__exit__was not called forwithblocks.Two places in Python/flowgraph.c created jump instructions without an exception handler after label_exception_targets() had already run:
Both now copy the handler of the original jump.
KeyboardInterruptnot handled while evaluating a tightwhileloop condition on Python 3.13 #130279