From 34f2140cae19298eda3ebb4463060579facd835b Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Fri, 16 Nov 2018 22:01:28 -0800 Subject: [PATCH] use StackWithLimit in interpreter --- src/common/stack.rs | 10 ++ src/func.rs | 152 +++++++++-------- src/runner.rs | 367 ++++++++++++++--------------------------- src/validation/func.rs | 32 +++- 4 files changed, 247 insertions(+), 314 deletions(-) diff --git a/src/common/stack.rs b/src/common/stack.rs index 7a9d30d..3be930f 100644 --- a/src/common/stack.rs +++ b/src/common/stack.rs @@ -116,6 +116,16 @@ impl StackWithLimit { self.stack.get(index) } + /// mutable version of get_relative_to_top + /// + /// `bstack.get_relative_to_top(0)` gets the top of the stack + /// + /// `bstack.get_relative_to_top(1)` gets the item just below the stack + pub(crate) fn get_relative_to_top_mut(&mut self, depth: usize) -> Option<&mut T> { + let index = self.stack.len().checked_sub(1)?.checked_sub(depth)?; + self.stack.get_mut(index) + } + pub(crate) fn top(&self) -> Option<&T> { self.stack.last() } diff --git a/src/func.rs b/src/func.rs index 3df987f..ace42f8 100644 --- a/src/func.rs +++ b/src/func.rs @@ -1,15 +1,19 @@ #[allow(unused_imports)] use alloc::prelude::*; use alloc::rc::{Rc, Weak}; +use common::stack::{StackSize, StackWithLimit}; use core::fmt; -use parity_wasm::elements::Local; -use {Trap, Signature}; use host::Externals; -use runner::{check_function_args, Interpreter, InterpreterState}; -use value::RuntimeValue; -use types::ValueType; -use module::ModuleInstance; use isa; +use module::ModuleInstance; +use parity_wasm::elements::Local; +use runner::{ + check_function_args, FunctionContext, Interpreter, InterpreterState, RuntimeValueInternal, + DEFAULT_CALL_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT, +}; +use types::ValueType; +use value::RuntimeValue; +use {Signature, Trap}; /// Reference to a function (See [`FuncInstance`] for details). /// @@ -58,21 +62,12 @@ pub(crate) enum FuncInstanceInternal { impl fmt::Debug for FuncInstance { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.as_internal() { - &FuncInstanceInternal::Internal { - ref signature, - .. - } => { + &FuncInstanceInternal::Internal { ref signature, .. } => { // We can't write description of self.module here, because it generate // debug string for function instances and this will lead to infinite loop. - write!( - f, - "Internal {{ signature={:?} }}", - signature, - ) - } - &FuncInstanceInternal::Host { ref signature, .. } => { - write!(f, "Host {{ signature={:?} }}", signature) + write!(f, "Internal {{ signature={:?} }}", signature,) } + &FuncInstanceInternal::Host { ref signature, .. } => write!(f, "Host {{ signature={:?} }}", signature), } } } @@ -110,11 +105,7 @@ impl FuncInstance { &self.0 } - pub(crate) fn alloc_internal( - module: Weak, - signature: Rc, - body: FuncBody, - ) -> FuncRef { + pub(crate) fn alloc_internal(module: Weak, signature: Rc, body: FuncBody) -> FuncRef { let func = FuncInstanceInternal::Internal { signature, module: module, @@ -130,6 +121,36 @@ impl FuncInstance { } } + /// Invoke this function with extra configuration parameters. + /// + /// This is an experimental API + /// + /// # Errors + /// + /// Returns `Err` if `args` types is not match function [`signature`] or + /// if [`Trap`] at execution time occured. + /// + /// [`signature`]: #method.signature + /// [`Trap`]: #enum.Trap.html + pub fn invoke_configurable( + func: &FuncRef, + args: &[RuntimeValue], + externals: &mut E, + value_stack: StackWithLimit, + call_stack: StackWithLimit, + ) -> Result, Trap> { + check_function_args(func.signature(), &args)?; + match *func.as_internal() { + FuncInstanceInternal::Internal { .. } => { + let mut interpreter = Interpreter::new(value_stack, call_stack); + interpreter.start_execution(externals, func, args, func.signature().return_type()) + } + FuncInstanceInternal::Host { + ref host_func_index, .. + } => externals.invoke_index(*host_func_index, args.into()), + } + } + /// Invoke this function. /// /// # Errors @@ -144,17 +165,9 @@ impl FuncInstance { args: &[RuntimeValue], externals: &mut E, ) -> Result, Trap> { - check_function_args(func.signature(), &args)?; - match *func.as_internal() { - FuncInstanceInternal::Internal { .. } => { - let mut interpreter = Interpreter::new(func, args)?; - interpreter.start_execution(externals) - } - FuncInstanceInternal::Host { - ref host_func_index, - .. - } => externals.invoke_index(*host_func_index, args.into()), - } + let value_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_VALUE_STACK_LIMIT)); + let call_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_CALL_STACK_LIMIT)); + Self::invoke_configurable(func, args, externals, value_stack, call_stack) } /// Invoke the function, get a resumable handle. This handle can then be used to [`start_execution`]. If a @@ -171,30 +184,26 @@ impl FuncInstance { /// [`Trap`]: #enum.Trap.html /// [`start_execution`]: struct.FuncInvocation.html#method.start_execution /// [`resume_execution`]: struct.FuncInvocation.html#method.resume_execution - pub fn invoke_resumable<'args>( - func: &FuncRef, - args: &'args [RuntimeValue], - ) -> Result, Trap> { + pub fn invoke_resumable<'args>(func: &FuncRef, args: &'args [RuntimeValue]) -> Result, Trap> { check_function_args(func.signature(), &args)?; match *func.as_internal() { FuncInstanceInternal::Internal { .. } => { - let interpreter = Interpreter::new(func, args)?; + let value_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_VALUE_STACK_LIMIT)); + let call_stack = StackWithLimit::with_size(StackSize::from_element_count(DEFAULT_CALL_STACK_LIMIT)); + let interpreter = Interpreter::new(value_stack, call_stack); Ok(FuncInvocation { kind: FuncInvocationKind::Internal(interpreter), }) } FuncInstanceInternal::Host { - ref host_func_index, - .. - } => { - Ok(FuncInvocation { - kind: FuncInvocationKind::Host { - args, - host_func_index: *host_func_index, - finished: false, - }, - }) - }, + ref host_func_index, .. + } => Ok(FuncInvocation { + kind: FuncInvocationKind::Host { + args, + host_func_index: *host_func_index, + finished: false, + }, + }), } } } @@ -239,7 +248,7 @@ enum FuncInvocationKind<'args> { Host { args: &'args [RuntimeValue], host_func_index: usize, - finished: bool + finished: bool, }, } @@ -255,32 +264,40 @@ impl<'args> FuncInvocation<'args> { /// If the invocation is resumable, the expected return value type to be feed back in. pub fn resumable_value_type(&self) -> Option { match &self.kind { - &FuncInvocationKind::Internal(ref interpreter) => { - match interpreter.state() { - &InterpreterState::Resumable(ref value_type) => value_type.clone(), - _ => None, - } + &FuncInvocationKind::Internal(ref interpreter) => match interpreter.state() { + &InterpreterState::Resumable(ref value_type) => value_type.clone(), + _ => None, }, &FuncInvocationKind::Host { .. } => None, } } /// Start the invocation execution. - pub fn start_execution<'externals, E: Externals + 'externals>(&mut self, externals: &'externals mut E) -> Result, ResumableError> { + pub fn start_execution<'externals, E: Externals + 'externals>( + &mut self, + externals: &'externals mut E, + func: &FuncRef, + args: &[RuntimeValue], + return_type: Option, + ) -> Result, ResumableError> { match self.kind { FuncInvocationKind::Internal(ref mut interpreter) => { if interpreter.state() != &InterpreterState::Initialized { return Err(ResumableError::AlreadyStarted); } - Ok(interpreter.start_execution(externals)?) - }, - FuncInvocationKind::Host { ref args, ref mut finished, ref host_func_index } => { + Ok(interpreter.start_execution(externals, func, args, return_type)?) + } + FuncInvocationKind::Host { + ref args, + ref mut finished, + ref host_func_index, + } => { if *finished { return Err(ResumableError::AlreadyStarted); } *finished = true; Ok(externals.invoke_index(*host_func_index, args.clone().into())?) - }, + } } } @@ -292,17 +309,22 @@ impl<'args> FuncInvocation<'args> { /// /// [`resumable_value_type`]: #method.resumable_value_type /// [`is_resumable`]: #method.is_resumable - pub fn resume_execution<'externals, E: Externals + 'externals>(&mut self, return_val: Option, externals: &'externals mut E) -> Result, ResumableError> { + pub fn resume_execution<'externals, E: Externals + 'externals>( + &mut self, + return_val: Option, + externals: &'externals mut E, + return_type: Option, + ) -> Result, ResumableError> { match self.kind { FuncInvocationKind::Internal(ref mut interpreter) => { if !interpreter.state().is_resumable() { return Err(ResumableError::AlreadyStarted); } - Ok(interpreter.resume_execution(return_val, externals)?) - }, + Ok(interpreter.resume_execution(return_val, externals, return_type)?) + } FuncInvocationKind::Host { .. } => { return Err(ResumableError::NotResumable); - }, + } } } } diff --git a/src/runner.rs b/src/runner.rs index fcdd765..4e03882 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -1,5 +1,6 @@ #[allow(unused_imports)] use alloc::prelude::*; +use common::stack::{StackOverflow, StackWithLimit}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use core::fmt; use core::ops; @@ -13,8 +14,8 @@ use module::ModuleRef; use nan_preserving_float::{F32, F64}; use parity_wasm::elements::Local; use value::{ - ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, - TryTruncateInto, WrapInto, + ArithmeticOps, ExtendInto, Float, Integer, LittleEndianConvert, RuntimeValue, TransmuteInto, TryTruncateInto, + WrapInto, }; use {Signature, Trap, TrapKind, ValueType}; @@ -36,7 +37,7 @@ pub const DEFAULT_CALL_STACK_LIMIT: usize = 64 * 1024; /// at these boundaries. #[derive(Copy, Clone, Debug, PartialEq, Default)] #[repr(transparent)] -struct RuntimeValueInternal(pub u64); +pub struct RuntimeValueInternal(pub u64); impl RuntimeValueInternal { pub fn with_type(self, ty: ValueType) -> RuntimeValue { @@ -166,54 +167,52 @@ enum RunResult { /// Function interpreter. pub struct Interpreter { value_stack: ValueStack, - call_stack: Vec, - return_type: Option, + call_stack: StackWithLimit, state: InterpreterState, } impl Interpreter { - pub fn new(func: &FuncRef, args: &[RuntimeValue]) -> Result { - let mut value_stack = ValueStack::with_limit(DEFAULT_VALUE_STACK_LIMIT); - for &arg in args { - let arg = arg.into(); - value_stack.push(arg).map_err( - // There is not enough space for pushing initial arguments. - // Weird, but bail out anyway. - |_| Trap::from(TrapKind::StackOverflow), - )?; - } - - let mut call_stack = Vec::new(); - let initial_frame = FunctionContext::new(func.clone()); - call_stack.push(initial_frame); - - let return_type = func.signature().return_type(); - - Ok(Interpreter { - value_stack, + pub fn new( + value_stack: StackWithLimit, + call_stack: StackWithLimit, + ) -> Interpreter { + Interpreter { + value_stack: ValueStack(value_stack), call_stack, - return_type, state: InterpreterState::Initialized, - }) + } } pub fn state(&self) -> &InterpreterState { &self.state } - pub fn start_execution<'a, E: Externals + 'a>( + pub fn start_execution( &mut self, - externals: &'a mut E, + externals: &mut E, + func: &FuncRef, + args: &[RuntimeValue], + return_type: Option, ) -> Result, Trap> { + debug_assert_eq!(func.signature().return_type(), return_type); + // Ensure that the VM has not been executed. This is checked in `FuncInvocation::start_execution`. assert!(self.state == InterpreterState::Initialized); + // Add initial args to value stack + for arg in args { + self.value_stack.push(RuntimeValueInternal::from(*arg))?; + } + + // Add initial frame to call stack + self.call_stack + .push(FunctionContext::new(func.clone())) + .map_err(|_| TrapKind::StackOverflow)?; + self.state = InterpreterState::Started; self.run_interpreter_loop(externals)?; - let opt_return_value = self - .return_type - .map(|vt| self.value_stack.pop().with_type(vt)); + let opt_return_value = return_type.map(|vt| self.value_stack.pop().with_type(vt)); // Ensure that stack is empty after the execution. This is guaranteed by the validation properties. assert!(self.value_stack.len() == 0); @@ -225,6 +224,7 @@ impl Interpreter { &mut self, return_val: Option, externals: &'a mut E, + return_type: Option, ) -> Result, Trap> { use core::mem::swap; @@ -235,16 +235,12 @@ impl Interpreter { swap(&mut self.state, &mut resumable_state); if let Some(return_val) = return_val { - self.value_stack - .push(return_val.into()) - .map_err(Trap::new)?; + self.value_stack.push(return_val.into()).map_err(Trap::new)?; } self.run_interpreter_loop(externals)?; - let opt_return_value = self - .return_type - .map(|vt| self.value_stack.pop().with_type(vt)); + let opt_return_value = return_type.map(|vt| self.value_stack.pop().with_type(vt)); // Ensure that stack is empty after the execution. This is guaranteed by the validation properties. assert!(self.value_stack.len() == 0); @@ -252,20 +248,16 @@ impl Interpreter { Ok(opt_return_value) } - fn run_interpreter_loop<'a, E: Externals + 'a>( - &mut self, - externals: &'a mut E, - ) -> Result<(), Trap> { + fn run_interpreter_loop<'a, E: Externals + 'a>(&mut self, externals: &'a mut E) -> Result<(), Trap> { loop { - let mut function_context = self.call_stack.pop().expect( - "on loop entry - not empty; on loop continue - checking for emptiness; qed", - ); + let mut function_context = self + .call_stack + .pop() + .expect("on loop entry - not empty; on loop continue - checking for emptiness; qed"); let function_ref = function_context.function.clone(); let function_body = function_ref .body() - .expect( - "Host functions checked in function_return below; Internal functions always have a body; qed" - ); + .expect("Host functions checked in function_return below; Internal functions always have a body; qed"); if !function_context.is_initialized() { // Initialize stack frame for the function call. @@ -278,40 +270,33 @@ impl Interpreter { match function_return { RunResult::Return => { - if self.call_stack.last().is_none() { + if self.call_stack.top().is_none() { // This was the last frame in the call stack. This means we // are done executing. return Ok(()); } } RunResult::NestedCall(nested_func) => { - if self.call_stack.len() + 1 >= DEFAULT_CALL_STACK_LIMIT { - return Err(TrapKind::StackOverflow.into()); - } - match *nested_func.as_internal() { FuncInstanceInternal::Internal { .. } => { let nested_context = FunctionContext::new(nested_func.clone()); - self.call_stack.push(function_context); - self.call_stack.push(nested_context); + self.call_stack.push(function_context)?; + self.call_stack.push(nested_context)?; } FuncInstanceInternal::Host { ref signature, .. } => { let args = prepare_function_args(signature, &mut self.value_stack); // We push the function context first. If the VM is not resumable, it does no harm. If it is, we then save the context here. - self.call_stack.push(function_context); + self.call_stack.push(function_context)?; - let return_val = - match FuncInstance::invoke(&nested_func, &args, externals) { - Ok(val) => val, - Err(trap) => { - if trap.kind().is_host() { - self.state = InterpreterState::Resumable( - nested_func.signature().return_type(), - ); - } - return Err(trap); + let return_val = match FuncInstance::invoke(&nested_func, &args, externals) { + Ok(val) => val, + Err(trap) => { + if trap.kind().is_host() { + self.state = InterpreterState::Resumable(nested_func.signature().return_type()); } - }; + return Err(trap); + } + }; // Check if `return_val` matches the signature. let value_ty = return_val.as_ref().map(|val| val.value_type()); @@ -321,9 +306,7 @@ impl Interpreter { } if let Some(return_val) = return_val { - self.value_stack - .push(return_val.into()) - .map_err(Trap::new)?; + self.value_stack.push(return_val.into()).map_err(Trap::new)?; } } } @@ -397,52 +380,26 @@ impl Interpreter { &isa::Instruction::I64Load(offset) => self.run_load::(context, offset), &isa::Instruction::F32Load(offset) => self.run_load::(context, offset), &isa::Instruction::F64Load(offset) => self.run_load::(context, offset), - &isa::Instruction::I32Load8S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I32Load8U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I32Load16S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I32Load16U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load8S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load8U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load16S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load16U(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load32S(offset) => { - self.run_load_extend::(context, offset) - } - &isa::Instruction::I64Load32U(offset) => { - self.run_load_extend::(context, offset) - } + &isa::Instruction::I32Load8S(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I32Load8U(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I32Load16S(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I32Load16U(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I64Load8S(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I64Load8U(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I64Load16S(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I64Load16U(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I64Load32S(offset) => self.run_load_extend::(context, offset), + &isa::Instruction::I64Load32U(offset) => self.run_load_extend::(context, offset), &isa::Instruction::I32Store(offset) => self.run_store::(context, offset), &isa::Instruction::I64Store(offset) => self.run_store::(context, offset), &isa::Instruction::F32Store(offset) => self.run_store::(context, offset), &isa::Instruction::F64Store(offset) => self.run_store::(context, offset), &isa::Instruction::I32Store8(offset) => self.run_store_wrap::(context, offset), - &isa::Instruction::I32Store16(offset) => { - self.run_store_wrap::(context, offset) - } + &isa::Instruction::I32Store16(offset) => self.run_store_wrap::(context, offset), &isa::Instruction::I64Store8(offset) => self.run_store_wrap::(context, offset), - &isa::Instruction::I64Store16(offset) => { - self.run_store_wrap::(context, offset) - } - &isa::Instruction::I64Store32(offset) => { - self.run_store_wrap::(context, offset) - } + &isa::Instruction::I64Store16(offset) => self.run_store_wrap::(context, offset), + &isa::Instruction::I64Store32(offset) => self.run_store_wrap::(context, offset), &isa::Instruction::CurrentMemory => self.run_current_memory(context), &isa::Instruction::GrowMemory => self.run_grow_memory(context), @@ -587,18 +544,11 @@ impl Interpreter { } } - fn run_unreachable( - &mut self, - _context: &mut FunctionContext, - ) -> Result { + fn run_unreachable(&mut self, _context: &mut FunctionContext) -> Result { Err(TrapKind::Unreachable) } - fn run_br( - &mut self, - _context: &mut FunctionContext, - target: isa::Target, - ) -> Result { + fn run_br(&mut self, _context: &mut FunctionContext, target: isa::Target) -> Result { Ok(InstructionOutcome::Branch(target)) } @@ -632,11 +582,7 @@ impl Interpreter { Ok(InstructionOutcome::Return(drop_keep)) } - fn run_call( - &mut self, - context: &mut FunctionContext, - func_idx: u32, - ) -> Result { + fn run_call(&mut self, context: &mut FunctionContext, func_idx: u32) -> Result { let func = context .module() .func_by_index(func_idx) @@ -706,11 +652,7 @@ impl Interpreter { Ok(InstructionOutcome::RunNextInstruction) } - fn run_get_global( - &mut self, - context: &mut FunctionContext, - index: u32, - ) -> Result { + fn run_get_global(&mut self, context: &mut FunctionContext, index: u32) -> Result { let global = context .module() .global_by_index(index) @@ -720,11 +662,7 @@ impl Interpreter { Ok(InstructionOutcome::RunNextInstruction) } - fn run_set_global( - &mut self, - context: &mut FunctionContext, - index: u32, - ) -> Result { + fn run_set_global(&mut self, context: &mut FunctionContext, index: u32) -> Result { let val = self.value_stack.pop(); let global = context .module() @@ -736,23 +674,15 @@ impl Interpreter { Ok(InstructionOutcome::RunNextInstruction) } - fn run_load( - &mut self, - context: &mut FunctionContext, - offset: u32, - ) -> Result + fn run_load(&mut self, context: &mut FunctionContext, offset: u32) -> Result where RuntimeValueInternal: From, T: LittleEndianConvert, { let raw_address = self.value_stack.pop_as(); let address = effective_address(offset, raw_address)?; - let m = context - .memory() - .expect("Due to validation memory should exists"); - let n: T = m - .get_value(address) - .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; + let m = context.memory().expect("Due to validation memory should exists"); + let n: T = m.get_value(address).map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; self.value_stack.push(n.into())?; Ok(InstructionOutcome::RunNextInstruction) } @@ -769,12 +699,8 @@ impl Interpreter { { let raw_address = self.value_stack.pop_as(); let address = effective_address(offset, raw_address)?; - let m = context - .memory() - .expect("Due to validation memory should exists"); - let v: T = m - .get_value(address) - .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; + let m = context.memory().expect("Due to validation memory should exists"); + let v: T = m.get_value(address).map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; let stack_value: U = v.extend_into(); self.value_stack .push(stack_value.into()) @@ -782,11 +708,7 @@ impl Interpreter { .map(|_| InstructionOutcome::RunNextInstruction) } - fn run_store( - &mut self, - context: &mut FunctionContext, - offset: u32, - ) -> Result + fn run_store(&mut self, context: &mut FunctionContext, offset: u32) -> Result where T: FromRuntimeValueInternal, T: LittleEndianConvert, @@ -795,9 +717,7 @@ impl Interpreter { let raw_address = self.value_stack.pop_as::(); let address = effective_address(offset, raw_address)?; - let m = context - .memory() - .expect("Due to validation memory should exists"); + let m = context.memory().expect("Due to validation memory should exists"); m.set_value(address, stack_value) .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; Ok(InstructionOutcome::RunNextInstruction) @@ -817,34 +737,22 @@ impl Interpreter { let stack_value = stack_value.wrap_into(); let raw_address = self.value_stack.pop_as::(); let address = effective_address(offset, raw_address)?; - let m = context - .memory() - .expect("Due to validation memory should exists"); + let m = context.memory().expect("Due to validation memory should exists"); m.set_value(address, stack_value) .map_err(|_| TrapKind::MemoryAccessOutOfBounds)?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_current_memory( - &mut self, - context: &mut FunctionContext, - ) -> Result { - let m = context - .memory() - .expect("Due to validation memory should exists"); + fn run_current_memory(&mut self, context: &mut FunctionContext) -> Result { + let m = context.memory().expect("Due to validation memory should exists"); let s = m.current_size().0; self.value_stack.push(RuntimeValueInternal(s as _))?; Ok(InstructionOutcome::RunNextInstruction) } - fn run_grow_memory( - &mut self, - context: &mut FunctionContext, - ) -> Result { + fn run_grow_memory(&mut self, context: &mut FunctionContext) -> Result { let pages: u32 = self.value_stack.pop_as(); - let m = context - .memory() - .expect("Due to validation memory should exists"); + let m = context.memory().expect("Due to validation memory should exists"); let m = match m.grow(Pages(pages as usize)) { Ok(Pages(new_size)) => new_size as u32, Err(_) => u32::MAX, // Returns -1 (or 0xFFFFFFFF) in case of error. @@ -1247,7 +1155,7 @@ impl Interpreter { } /// Function execution context. -struct FunctionContext { +pub struct FunctionContext { /// Is context initialized. pub is_initialized: bool, /// Internal function reference. @@ -1259,7 +1167,7 @@ struct FunctionContext { } impl FunctionContext { - pub fn new(function: FuncRef) -> Self { + pub(crate) fn new(function: FuncRef) -> Self { let module = match function.as_internal() { FuncInstanceInternal::Internal { module, .. } => module.upgrade().expect("module deallocated"), FuncInstanceInternal::Host { .. } => panic!("Host functions can't be called as internally defined functions; Thus FunctionContext can be created only with internally defined functions; qed"), @@ -1278,11 +1186,7 @@ impl FunctionContext { self.is_initialized } - pub fn initialize( - &mut self, - locals: &[Local], - value_stack: &mut ValueStack, - ) -> Result<(), TrapKind> { + fn initialize(&mut self, locals: &[Local], value_stack: &mut ValueStack) -> Result<(), TrapKind> { debug_assert!(!self.is_initialized); let num_locals = locals.iter().map(|l| l.count() as usize).sum(); @@ -1290,9 +1194,7 @@ impl FunctionContext { // TODO: Replace with extend. for local in locals { - value_stack - .push(local) - .map_err(|_| TrapKind::StackOverflow)?; + value_stack.push(local).map_err(|_| TrapKind::StackOverflow)?; } self.is_initialized = true; @@ -1321,10 +1223,7 @@ fn effective_address(address: u32, offset: u32) -> Result { } } -fn prepare_function_args( - signature: &Signature, - caller_stack: &mut ValueStack, -) -> Vec { +fn prepare_function_args(signature: &Signature, caller_stack: &mut ValueStack) -> Vec { let mut out = signature .params() .iter() @@ -1340,14 +1239,10 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu return Err(TrapKind::UnexpectedSignature.into()); } - if signature - .params() - .iter() - .zip(args) - .any(|(expected_type, param_value)| { - let actual_type = param_value.value_type(); - &actual_type != expected_type - }) { + if signature.params().iter().zip(args).any(|(expected_type, param_value)| { + let actual_type = param_value.value_type(); + &actual_type != expected_type + }) { return Err(TrapKind::UnexpectedSignature.into()); } @@ -1355,32 +1250,29 @@ pub fn check_function_args(signature: &Signature, args: &[RuntimeValue]) -> Resu } #[derive(Debug)] -struct ValueStack { - buf: Box<[RuntimeValueInternal]>, - /// Index of the first free place in the stack. - sp: usize, -} +struct ValueStack(StackWithLimit); impl ValueStack { - fn with_limit(limit: usize) -> ValueStack { - let mut buf = Vec::new(); - buf.resize(limit, RuntimeValueInternal(0)); - - ValueStack { - buf: buf.into_boxed_slice(), - sp: 0, - } - } - #[inline] fn drop_keep(&mut self, drop_keep: isa::DropKeep) { - if drop_keep.keep == isa::Keep::Single { - let top = *self.top(); - *self.pick_mut(drop_keep.drop as usize + 1) = top; - } + match drop_keep.keep { + isa::Keep::Single => { + let top = *self.top(); // takes a copy + let pick = self + .0 + .get_relative_to_top_mut(drop_keep.drop as usize) + .expect("pre-validated"); + *pick = top; + } + isa::Keep::None => {} + }; + self.drop_many(drop_keep.drop as usize); + } - let cur_stack_len = self.len(); - self.sp = cur_stack_len - drop_keep.drop as usize; + fn drop_many(&mut self, count: usize) { + debug_assert!(count <= self.len(), "Attempted to drop more items than were in stack."); + let new_len = self.0.len().checked_sub(count).unwrap_or(0); + self.0.truncate(new_len); } #[inline] @@ -1404,13 +1296,7 @@ impl ValueStack { } #[inline] - fn pop_triple( - &mut self, - ) -> ( - RuntimeValueInternal, - RuntimeValueInternal, - RuntimeValueInternal, - ) { + fn pop_triple(&mut self) -> (RuntimeValueInternal, RuntimeValueInternal, RuntimeValueInternal) { let right = self.pop(); let mid = self.pop(); let left = self.pop(); @@ -1419,37 +1305,38 @@ impl ValueStack { #[inline] fn top(&self) -> &RuntimeValueInternal { - self.pick(1) - } - - fn pick(&self, depth: usize) -> &RuntimeValueInternal { - &self.buf[self.sp - depth] + self.0.top().expect("pre-validated") } #[inline] fn pick_mut(&mut self, depth: usize) -> &mut RuntimeValueInternal { - &mut self.buf[self.sp - depth] + self.0.get_relative_to_top_mut(depth - 1).expect("pre-validated") } #[inline] fn pop(&mut self) -> RuntimeValueInternal { - self.sp -= 1; - self.buf[self.sp] + self.0.pop().expect("pre-validated") } #[inline] fn push(&mut self, value: RuntimeValueInternal) -> Result<(), TrapKind> { - let cell = self - .buf - .get_mut(self.sp) - .ok_or_else(|| TrapKind::StackOverflow)?; - *cell = value; - self.sp += 1; - Ok(()) + self.0.push(value).map_err(TrapKind::from) } #[inline] fn len(&self) -> usize { - self.sp + self.0.len() + } +} + +impl From for TrapKind { + fn from(_: StackOverflow) -> TrapKind { + TrapKind::StackOverflow + } +} + +impl From for Trap { + fn from(_: StackOverflow) -> Trap { + Trap::new(TrapKind::StackOverflow) } } diff --git a/src/validation/func.rs b/src/validation/func.rs index 4f700bd..98ea15f 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -10,7 +10,7 @@ use validation::context::ModuleContext; use validation::util::Locals; use validation::Error; -use common::stack::StackWithLimit; +use common::stack::{StackSize, StackWithLimit}; use isa; /// Maximum number of entries in value stack per function. @@ -1575,8 +1575,8 @@ impl<'a> FunctionValidationContext<'a> { module: module, position: 0, locals: locals, - value_stack: StackWithLimit::with_limit(value_stack_limit), - frame_stack: StackWithLimit::with_limit(frame_stack_limit), + value_stack: StackWithLimit::with_size(StackSize::from_element_count(value_stack_limit)), + frame_stack: StackWithLimit::with_size(StackSize::from_element_count(frame_stack_limit)), return_type: return_type, sink: Sink::with_instruction_capacity(size_estimate), } @@ -1598,7 +1598,15 @@ fn make_top_frame_polymorphic( let frame = frame_stack .top_mut() .expect("make_top_frame_polymorphic is called with empty frame stack"); - value_stack.resize(frame.value_stack_len, StackValueType::Any); + + // shrink value stack to match top frame + debug_assert!(frame.value_stack_len <= value_stack.len()); + while value_stack.len() > frame.value_stack_len { + value_stack.pop().expect( + "frame.value_stack_len >= 0, value_stack.len() > frame.value_stack_len :. value_stack.len() > 0; qed", + ); + } + frame.polymorphic_stack = true; } @@ -1630,7 +1638,7 @@ fn pop_value( if value_stack.len() <= value_stack_min { return Err(Error("Trying to access parent frame stack values.".into())); } - value_stack.pop()? + value_stack.pop().ok_or_else(|| Error("non-empty stack expected".into()))? }; match actual_value { StackValueType::Specific(stack_value_type) if stack_value_type == value_type => { @@ -1678,7 +1686,10 @@ fn pop_label( // Don't pop frame yet. This is essential since we still might pop values from the value stack // and this in turn requires current frame to check whether or not we've reached // unreachable. - let block_type = frame_stack.top()?.block_type; + let block_type = frame_stack + .top() + .ok_or_else(|| Error("non-empty stack expected".into()))? + .block_type; match block_type { BlockType::NoResult => (), BlockType::Value(required_value_type) => { @@ -1690,7 +1701,9 @@ fn pop_label( } } - let frame = frame_stack.pop()?; + let frame = frame_stack + .pop() + .ok_or_else(|| Error("non-empty stack expected".into()))?; if value_stack.len() != frame.value_stack_len { return Err(Error(format!( "Unexpected stack height {}, expected {}", @@ -1712,7 +1725,9 @@ fn require_label( depth: u32, frame_stack: &StackWithLimit, ) -> Result<&BlockFrame, Error> { - Ok(frame_stack.get(depth as usize)?) + Ok(frame_stack + .get_relative_to_top(depth as usize) + .ok_or_else(|| Error("non-empty stack expected".into()))?) } fn require_target( @@ -1956,4 +1971,3 @@ impl Sink { self.ins } } -