diff --git a/lib/middleware-common/src/opcode_control.rs b/lib/middleware-common/src/opcode_control.rs index 9f4c9d7abb4..b97051b542d 100644 --- a/lib/middleware-common/src/opcode_control.rs +++ b/lib/middleware-common/src/opcode_control.rs @@ -7,18 +7,65 @@ use wasmer_runtime_core::{ use crate::runtime_breakpoints::{push_runtime_breakpoint, BREAKPOINT_VALUE_EXECUTION_FAILED}; +static FIELD_MEMORY_GROW_COUNT: InternalField = InternalField::allocate(); + static FIELD_OPERAND_BACKUP: InternalField = InternalField::allocate(); pub struct OpcodeControl { + pub max_memory_grow: usize, pub max_memory_grow_delta: usize, } impl OpcodeControl { - pub fn new(max_memory_grow_delta: usize) -> OpcodeControl { + pub fn new(max_memory_grow: usize, max_memory_grow_delta: usize) -> OpcodeControl { OpcodeControl { + max_memory_grow, max_memory_grow_delta, } } + + fn inject_memory_grow_count_limit(&mut self, sink: &mut EventSink) { + sink.push(Event::Internal(InternalEvent::GetInternal( + FIELD_MEMORY_GROW_COUNT.index() as _, + ))); + sink.push(Event::WasmOwned(Operator::I64Const { + value: self.max_memory_grow as i64, + })); + sink.push(Event::WasmOwned(Operator::I64GeU)); + sink.push(Event::WasmOwned(Operator::If { + ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType), + })); + push_runtime_breakpoint(sink, BREAKPOINT_VALUE_EXECUTION_FAILED); + sink.push(Event::WasmOwned(Operator::End)); + } + + fn inject_memory_grow_count_increment(&mut self, sink: &mut EventSink) { + sink.push(Event::Internal(InternalEvent::GetInternal( + FIELD_MEMORY_GROW_COUNT.index() as _, + ))); + sink.push(Event::WasmOwned(Operator::I64Const{ + value: 1 as i64, + })); + sink.push(Event::WasmOwned(Operator::I64Add)); + sink.push(Event::Internal(InternalEvent::SetInternal( + FIELD_MEMORY_GROW_COUNT.index() as _, + ))); + } + + fn inject_memory_grow_delta_limit(&mut self, sink: &mut EventSink) { + sink.push(Event::Internal(InternalEvent::GetInternal( + FIELD_OPERAND_BACKUP.index() as _, + ))); + sink.push(Event::WasmOwned(Operator::I64Const { + value: self.max_memory_grow_delta as i64, + })); + sink.push(Event::WasmOwned(Operator::I64GtU)); + sink.push(Event::WasmOwned(Operator::If { + ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType), + })); + push_runtime_breakpoint(sink, BREAKPOINT_VALUE_EXECUTION_FAILED); + sink.push(Event::WasmOwned(Operator::End)); + } } impl FunctionMiddleware for OpcodeControl { @@ -38,6 +85,11 @@ impl FunctionMiddleware for OpcodeControl { return Err("MemoryGrow must have memory index 0".to_string()); } + // Before attempting anything with memory.grow, the current memory.grow + // count is checked against the self.max_memory_grow limit. + self.inject_memory_grow_count_limit(sink); + self.inject_memory_grow_count_increment(sink); + // Backup the top of the stack (the parameter for memory.grow) in order to // duplicate it: once for the comparison against max_memory_grow_delta and // again for memory.grow itself, assuming the comparison passes. @@ -46,18 +98,7 @@ impl FunctionMiddleware for OpcodeControl { ))); // Set up the comparison against max_memory_grow_delta. - sink.push(Event::Internal(InternalEvent::GetInternal( - FIELD_OPERAND_BACKUP.index() as _, - ))); - sink.push(Event::WasmOwned(Operator::I32Const { - value: self.max_memory_grow_delta as i32, - })); - sink.push(Event::WasmOwned(Operator::I32GtU)); - sink.push(Event::WasmOwned(Operator::If { - ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType), - })); - push_runtime_breakpoint(sink, BREAKPOINT_VALUE_EXECUTION_FAILED); - sink.push(Event::WasmOwned(Operator::End)); + self.inject_memory_grow_delta_limit(sink); // Bring back the backed-up operand for memory.grow. sink.push(Event::Internal(InternalEvent::GetInternal( diff --git a/lib/runtime-c-api/src/instance.rs b/lib/runtime-c-api/src/instance.rs index 0faaddd996f..cf7587198f2 100644 --- a/lib/runtime-c-api/src/instance.rs +++ b/lib/runtime-c-api/src/instance.rs @@ -201,6 +201,7 @@ pub struct wasmer_compilation_options_t; pub struct CompilationOptions { pub gas_limit: u64, pub unmetered_locals: usize, + pub max_memory_grow: usize, pub max_memory_grow_delta: usize, pub opcode_trace: bool, pub metering: bool, @@ -266,7 +267,10 @@ pub unsafe fn prepare_middleware_chain_generator( )); } - chain.push(opcode_control::OpcodeControl::new(options.max_memory_grow_delta)); + chain.push(opcode_control::OpcodeControl::new( + options.max_memory_grow, + options.max_memory_grow_delta + )); // The RuntimeBreakpointHandler must be the last middleware in the chain (OpcodeTracer is // an exception since it does not alter the opcodes meaningfully.