mirror of
https://github.com/python/cpython.git
synced 2025-12-07 13:50:06 +00:00
gh-141976: Check stack bounds in JIT optimizer (GH-142201)
This commit is contained in:
parent
2dac9e6016
commit
b3bf212898
6 changed files with 212 additions and 6 deletions
|
|
@ -2115,6 +2115,7 @@ def test_validate_uop_unused_input(self):
|
|||
"""
|
||||
output = """
|
||||
case OP: {
|
||||
CHECK_STACK_BOUNDS(-1);
|
||||
stack_pointer += -1;
|
||||
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
|
||||
break;
|
||||
|
|
@ -2132,6 +2133,7 @@ def test_validate_uop_unused_input(self):
|
|||
"""
|
||||
output = """
|
||||
case OP: {
|
||||
CHECK_STACK_BOUNDS(-1);
|
||||
stack_pointer += -1;
|
||||
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
|
||||
break;
|
||||
|
|
@ -2153,6 +2155,7 @@ def test_validate_uop_unused_output(self):
|
|||
case OP: {
|
||||
JitOptRef foo;
|
||||
foo = NULL;
|
||||
CHECK_STACK_BOUNDS(1);
|
||||
stack_pointer[0] = foo;
|
||||
stack_pointer += 1;
|
||||
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
|
||||
|
|
@ -2172,6 +2175,7 @@ def test_validate_uop_unused_output(self):
|
|||
"""
|
||||
output = """
|
||||
case OP: {
|
||||
CHECK_STACK_BOUNDS(1);
|
||||
stack_pointer += 1;
|
||||
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Check against abstract stack overflow in the JIT optimizer.
|
||||
|
|
@ -144,10 +144,6 @@ incorrect_keys(PyObject *obj, uint32_t version)
|
|||
|
||||
#define CURRENT_FRAME_IS_INIT_SHIM() (ctx->frame->code == ((PyCodeObject *)&_Py_InitCleanup))
|
||||
|
||||
#define WITHIN_STACK_BOUNDS() \
|
||||
(CURRENT_FRAME_IS_INIT_SHIM() || (STACK_LEVEL() >= 0 && STACK_LEVEL() <= STACK_SIZE()))
|
||||
|
||||
|
||||
#define GETLOCAL(idx) ((ctx->frame->locals[idx]))
|
||||
|
||||
#define REPLACE_OP(INST, OP, ARG, OPERAND) \
|
||||
|
|
@ -192,6 +188,27 @@ incorrect_keys(PyObject *obj, uint32_t version)
|
|||
|
||||
#define JUMP_TO_LABEL(label) goto label;
|
||||
|
||||
static int
|
||||
check_stack_bounds(JitOptContext *ctx, JitOptRef *stack_pointer, int offset, int opcode)
|
||||
{
|
||||
int stack_level = (int)(stack_pointer + (offset) - ctx->frame->stack);
|
||||
int should_check = !CURRENT_FRAME_IS_INIT_SHIM() ||
|
||||
(opcode == _RETURN_VALUE) ||
|
||||
(opcode == _RETURN_GENERATOR) ||
|
||||
(opcode == _YIELD_VALUE);
|
||||
if (should_check && (stack_level < 0 || stack_level > STACK_SIZE())) {
|
||||
ctx->contradiction = true;
|
||||
ctx->done = true;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CHECK_STACK_BOUNDS(offset) \
|
||||
if (check_stack_bounds(ctx, stack_pointer, offset, opcode)) { \
|
||||
break; \
|
||||
} \
|
||||
|
||||
static int
|
||||
optimize_to_bool(
|
||||
_PyUOpInstruction *this_instr,
|
||||
|
|
|
|||
173
Python/optimizer_cases.c.h
generated
173
Python/optimizer_cases.c.h
generated
File diff suppressed because it is too large
Load diff
|
|
@ -445,7 +445,7 @@ def generate_abstract_interpreter(
|
|||
declare_variables(override, out, skip_inputs=False)
|
||||
else:
|
||||
declare_variables(uop, out, skip_inputs=True)
|
||||
stack = Stack()
|
||||
stack = Stack(check_stack_bounds=True)
|
||||
write_uop(override, uop, out, stack, debug, skip_inputs=(override is None))
|
||||
out.start_line()
|
||||
out.emit("break;\n")
|
||||
|
|
|
|||
|
|
@ -216,11 +216,12 @@ def array_or_scalar(var: StackItem | Local) -> str:
|
|||
return "array" if var.is_array() else "scalar"
|
||||
|
||||
class Stack:
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, check_stack_bounds: bool = False) -> None:
|
||||
self.base_offset = PointerOffset.zero()
|
||||
self.physical_sp = PointerOffset.zero()
|
||||
self.logical_sp = PointerOffset.zero()
|
||||
self.variables: list[Local] = []
|
||||
self.check_stack_bounds = check_stack_bounds
|
||||
|
||||
def drop(self, var: StackItem, check_liveness: bool) -> None:
|
||||
self.logical_sp = self.logical_sp.pop(var)
|
||||
|
|
@ -316,8 +317,17 @@ def save_variables(self, out: CWriter) -> None:
|
|||
self._print(out)
|
||||
var_offset = var_offset.push(var.item)
|
||||
|
||||
def stack_bound_check(self, out: CWriter) -> None:
|
||||
if not self.check_stack_bounds:
|
||||
return
|
||||
if self.physical_sp != self.logical_sp:
|
||||
diff = self.logical_sp - self.physical_sp
|
||||
out.start_line()
|
||||
out.emit(f"CHECK_STACK_BOUNDS({diff});\n")
|
||||
|
||||
def flush(self, out: CWriter) -> None:
|
||||
self._print(out)
|
||||
self.stack_bound_check(out)
|
||||
self.save_variables(out)
|
||||
self._save_physical_sp(out)
|
||||
out.start_line()
|
||||
|
|
@ -347,6 +357,7 @@ def copy(self) -> "Stack":
|
|||
other.physical_sp = self.physical_sp
|
||||
other.logical_sp = self.logical_sp
|
||||
other.variables = [var.copy() for var in self.variables]
|
||||
other.check_stack_bounds = self.check_stack_bounds
|
||||
return other
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue