From be80db14c432c621e44920f8fd95a3f3191aca9b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 1 Jul 2022 14:01:14 +0100 Subject: GH-94438: Account for NULLs on evaluation stack when jumping lines. (GH-94444) --- Lib/test/test_sys_settrace.py | 65 ++++++++++++++++++++-- .../2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst | 2 + Objects/frameobject.c | 56 ++++++++++++++----- 3 files changed, 106 insertions(+), 17 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 162fd83..fd2740e 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -2264,25 +2264,25 @@ class JumpTestCase(unittest.TestCase): output.append(2) output.append(3) - @jump_test(1, 3, [], (ValueError, 'depth')) + @jump_test(1, 3, [], (ValueError, 'stack')) def test_no_jump_forwards_into_with_block(output): output.append(1) with tracecontext(output, 2): output.append(3) - @async_jump_test(1, 3, [], (ValueError, 'depth')) + @async_jump_test(1, 3, [], (ValueError, 'stack')) async def test_no_jump_forwards_into_async_with_block(output): output.append(1) async with asynctracecontext(output, 2): output.append(3) - @jump_test(3, 2, [1, 2, -1], (ValueError, 'depth')) + @jump_test(3, 2, [1, 2, -1], (ValueError, 'stack')) def test_no_jump_backwards_into_with_block(output): with tracecontext(output, 1): output.append(2) output.append(3) - @async_jump_test(3, 2, [1, 2, -1], (ValueError, 'depth')) + @async_jump_test(3, 2, [1, 2, -1], (ValueError, 'stack')) async def test_no_jump_backwards_into_async_with_block(output): async with asynctracecontext(output, 1): output.append(2) @@ -2584,6 +2584,63 @@ output.append(4) output.append(7) output.append(8) + # checking for segfaults. + @jump_test(3, 7, [], error=(ValueError, "stack")) + def test_jump_with_null_on_stack_load_global(output): + a = 1 + print( + output.append(3) + ) + output.append(5) + ( + ( # 7 + a + + + 10 + ) + + + 13 + ) + output.append(15) + + # checking for segfaults. + @jump_test(4, 8, [], error=(ValueError, "stack")) + def test_jump_with_null_on_stack_push_null(output): + a = 1 + f = print + f( + output.append(4) + ) + output.append(6) + ( + ( # 8 + a + + + 11 + ) + + + 14 + ) + output.append(16) + + # checking for segfaults. + @jump_test(3, 7, [], error=(ValueError, "stack")) + def test_jump_with_null_on_stack_load_attr(output): + a = 1 + list.append( + output, 3 + ) + output.append(5) + ( + ( # 7 + a + + + 10 + ) + + + 13 + ) + output.append(15) class TestExtendedArgs(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst new file mode 100644 index 0000000..b08dd8f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-30-15-07-26.gh-issue-94438.btzHSk.rst @@ -0,0 +1,2 @@ +Account for instructions that can push NULL to the stack when setting line +number in a frame. Prevents some (unlikely) crashes. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 44cc062..34a6c46 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -137,9 +137,24 @@ typedef enum kind { Iterator = 1, Except = 2, Object = 3, + Null = 4, } Kind; -#define BITS_PER_BLOCK 2 +static int +compatible_kind(Kind from, Kind to) { + if (to == 0) { + return 0; + } + if (to == Object) { + return from != Null; + } + if (to == Null) { + return 1; + } + return from == to; +} + +#define BITS_PER_BLOCK 3 #define UNINITIALIZED -2 #define OVERFLOWED -1 @@ -298,6 +313,31 @@ mark_stacks(PyCodeObject *code_obj, int len) case RERAISE: /* End of block */ break; + case PUSH_NULL: + next_stack = push_value(next_stack, Null); + stacks[i+1] = next_stack; + break; + case LOAD_GLOBAL: + { + int j = get_arg(code, i); + if (j & 1) { + next_stack = push_value(next_stack, Null); + } + next_stack = push_value(next_stack, Object); + stacks[i+1] = next_stack; + break; + } + case LOAD_ATTR: + { + int j = get_arg(code, i); + if (j & 1) { + next_stack = pop_value(next_stack); + next_stack = push_value(next_stack, Null); + next_stack = push_value(next_stack, Object); + } + stacks[i+1] = next_stack; + break; + } default: { int delta = PyCompile_OpcodeStackEffect(opcode, _Py_OPARG(code[i])); @@ -319,17 +359,6 @@ mark_stacks(PyCodeObject *code_obj, int len) } static int -compatible_kind(Kind from, Kind to) { - if (to == 0) { - return 0; - } - if (to == Object) { - return 1; - } - return from == to; -} - -static int compatible_stack(int64_t from_stack, int64_t to_stack) { if (from_stack < 0 || to_stack < 0) { @@ -365,7 +394,8 @@ explain_incompatible_stack(int64_t to_stack) case Except: return "can't jump into an 'except' block as there's no exception"; case Object: - return "differing stack depth"; + case Null: + return "incompatible stacks"; case Iterator: return "can't jump into the body of a for loop"; default: -- cgit v0.12