New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
frame.setlineno has serious flaws. #94438
Comments
…nes. (pythonGH-94444) (cherry picked from commit be80db1) Co-authored-by: Mark Shannon <mark@hotpy.org>
Moving this back to release blocker because apparently, this could end in many changes. I am missing some context here on what this is affecting so I changed it from deferred blocker to release blocker if we think we can delay this to 3.12, please, say so :) |
…O and POP_EXCEPT cases are no longer reachable
After failing to write a test that will crash on this, I analysed the code and realised that the "exception handling opcode" cases of the switch are no longer reachable - there is nothing that will initialise their stack[] entries, so they get skipped in the I created a PR to use my favourite macro in these cases: #94582 We could backport it, but we don't have to. |
I'm assuming this refers to the mark_stacks loop again, which is where stacks are produced. The good news is that it looks like it just happens to work by accident. There is no case in the switch for the CACHE opcode, so it goes to the default case. Since the stack_effect of CACHE is 0, it just copies We could make it more explicit like this:
thoughts? |
…ACHE opcodes which leaves the stack unchanged
Don't cache entries get populated at specialization time with data that overwrites the CACHE opcode? I don't think we're keeping around 0s in the bytecode that aren't subject to change. If I'm understanding right, the problem comes from misinterpreting that specialized cache data as if it's some bytecode that has a stack effect. It looks like the dis module looks up |
Ah I see! Would you like to try? (I'm signing off for the night soon.) |
I don't think I'm going to have time in the next couple of days to come up with a decent test case and PR and everything, but I am thinking that something vaguely like this could work: @@ -213,12 +213,15 @@ mark_stacks(PyCodeObject *code_obj, int len)
int todo = 1;
while (todo) {
todo = 0;
- for (i = 0; i < len; i++) {
+ int ncaches;
+ for (i = 0; i < len; i += (1 + ncaches)) {
+ ncaches = 0;
int64_t next_stack = stacks[i];
if (next_stack == UNINITIALIZED) {
continue;
}
- opcode = _Py_OPCODE(code[i]);
+ opcode = _PyOpcode_Deopt[_Py_OPCODE(code[i])];
+ ncaches = _PyOpcode_Caches[opcode];
switch (opcode) {
case JUMP_IF_FALSE_OR_POP:
case JUMP_IF_TRUE_OR_POP: |
…POP_EXCEPT cases are no longer reachable (GH-94582)
…EXC_INFO and POP_EXCEPT cases are no longer reachable (pythonGH-94582) (cherry picked from commit 50b9a77) Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com>
@iritkatriel is correct... nothing needs to be done about this. So I think this issue can be closed? |
Does it make sense to add some test that exercises this function on highly specialised/warmed up code? I think all the tests just run some function a small number of times. |
Ah, my mistake, I missed the |
Well, the function is designed to totally ignore warmed-up code and just use the deoptimized string. So new tests would keep us from accidentally using it in the future, but I'm pretty sure other stuff here would start breaking much sooner (tons of instructions would be hitting the Perhaps it would just be better to |
Let’s close then. |
Reopening because extended arguments aren't handled correctly. :( |
…k_stacks (pythonGH-95110) (cherry picked from commit e4d3a96) Co-authored-by: Brandt Bucher <brandtbucher@microsoft.com>
Removing release blocker status since all known crashes are fixed. I'm still going to leave this open, thoough, since the four "is None" jumps aren't handled yet (which just means that we aren't able to jump to some valid locations). |
I just noticed that the switch in mark_stacks doesn't have cases for POP_JUMP_IF_NONE/POP_JUMP_IF_NOT_NONE. |
Yep. See my above comment. :) |
ah yeah :) |
Shall we nevertheless close this, and open a more specific issue? |
Yes. |
The
frame_setlineno
function works in in stages:The first steps is faulty (I think, I haven't demonstrated this) as it might be possible to jump to an instruction involved in frame creation. This should be easy to fix using the new
_co_firsttraceable
field.The second step has (at least) three flaws:
NULL
s on the stack, making it possible to jump from a stack withNULL
s to one that cannot handleNULL
s.PUSH_EXC_INFO
pushes three values. It only pushes one.Setting the line number of a frame is only possible in the debugger, so this isn't as terrible as might appear, but it definitely needs fixing.
The text was updated successfully, but these errors were encountered: