Skip to content

Commit

Permalink
[3.11] GH-94438: Backport GH-94444 (#94486)
Browse files Browse the repository at this point in the history
* Account for NULLs on evaluation stack when jumping lines.
  • Loading branch information
markshannon authored Jul 1, 2022
1 parent 9fa9661 commit 02b30a8
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 17 deletions.
65 changes: 61 additions & 4 deletions Lib/test/test_sys_settrace.py
Original file line number Diff line number Diff line change
Expand Up @@ -2264,25 +2264,25 @@ async def test_no_jump_backwards_into_async_for_block(output):
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)
Expand Down Expand Up @@ -2584,6 +2584,63 @@ async def test_jump_backward_over_async_listcomp_v2(output):
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):

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
48 changes: 35 additions & 13 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -298,6 +313,23 @@ 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:
if (_Py_OPARG(code[i]) & 1) {
next_stack = push_value(next_stack, Null);
}
next_stack = push_value(next_stack, Object);
stacks[i+1] = next_stack;
break;
case LOAD_METHOD:
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]));
Expand All @@ -318,17 +350,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
return stacks;
}

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)
{
Expand Down Expand Up @@ -365,7 +386,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:
Expand Down

0 comments on commit 02b30a8

Please sign in to comment.