From 47dd23f6018e771fb5fb3aaa0c056c448f95145e Mon Sep 17 00:00:00 2001 From: Andrew Dirksen Date: Fri, 16 Nov 2018 13:17:59 -0800 Subject: [PATCH] make StackWithLimit pre-allocate --- src/common/stack.rs | 286 ++++++++++++++++++++++++++-------- src/validation/func.rs | 339 +++++++++++++++++------------------------ src/validation/mod.rs | 133 +++++++--------- 3 files changed, 413 insertions(+), 345 deletions(-) diff --git a/src/common/stack.rs b/src/common/stack.rs index a96af35..692af3b 100644 --- a/src/common/stack.rs +++ b/src/common/stack.rs @@ -1,88 +1,248 @@ -#[allow(unused_imports)] -use alloc::prelude::*; +mod ol { + #[allow(unused_imports)] + use alloc::prelude::*; -#[cfg(feature = "std")] -use std::error; -use core::fmt; + use core::fmt; + #[cfg(feature = "std")] + use std::error; -#[derive(Debug)] -pub struct Error(String); + #[derive(Debug)] + pub struct Error(String); -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } + } + + #[cfg(feature = "std")] + impl error::Error for Error { + fn description(&self) -> &str { + &self.0 + } + } + + /// Stack with limit. + #[derive(Debug)] + pub struct StackWithLimit + where + T: Clone, + { + /// Stack values. + values: Vec, + /// Stack limit (maximal stack len). + limit: usize, + } + + impl StackWithLimit + where + T: Clone, + { + pub fn with_limit(limit: usize) -> Self { + StackWithLimit { + values: Vec::new(), + limit: limit, + } + } + + pub fn is_empty(&self) -> bool { + self.values.is_empty() + } + + pub fn len(&self) -> usize { + self.values.len() + } + + pub fn top(&self) -> Result<&T, Error> { + self.values + .last() + .ok_or_else(|| Error("non-empty stack expected".into())) + } + + pub fn top_mut(&mut self) -> Result<&mut T, Error> { + self.values + .last_mut() + .ok_or_else(|| Error("non-empty stack expected".into())) + } + + // Not the same as vector.get + pub fn get(&self, index: usize) -> Result<&T, Error> { + if index >= self.values.len() { + return Err(Error(format!( + "trying to get value at position {} on stack of size {}", + index, + self.values.len() + ))); + } + + Ok(self + .values + .get(self.values.len() - 1 - index) + .expect("checked couple of lines above")) + } + + pub fn push(&mut self, value: T) -> Result<(), Error> { + if self.values.len() >= self.limit { + return Err(Error(format!("exceeded stack limit {}", self.limit))); + } + + self.values.push(value); + Ok(()) + } + + pub fn pop(&mut self) -> Result { + self.values + .pop() + .ok_or_else(|| Error("non-empty stack expected".into())) + } + + pub fn resize(&mut self, new_size: usize, dummy: T) { + debug_assert!(new_size <= self.values.len()); + self.values.resize(new_size, dummy); + } } } -#[cfg(feature = "std")] -impl error::Error for Error { - fn description(&self) -> &str { - &self.0 - } -} +#[derive(Copy, Clone, Debug)] +pub struct StackOverflow; -/// Stack with limit. +// impl From for TrapKind { +// fn from(_: StackOverflow) -> TrapKind { +// TrapKind::StackOverflow +// } +// } + +// impl From for Trap { +// fn from(_: StackOverflow) -> Trap { +// Trap::new(TrapKind::StackOverflow) +// } +// } + +// TODO: impl constructors +struct Limit(usize); +struct InitialSize(usize); + +/// Pre-allocated, growable stack with upper bound on size. +/// +/// StackWithLimit is guaranteed never to grow larger than a set limit. +/// When limit is reached attempting to push to the stack will return +/// `Err(StackOverflow)`. +/// +/// In addition to the limit. Initial stack size is configurable. +/// `StackWithLimit` will start out with initial size, but grow if necessary. #[derive(Debug)] -pub struct StackWithLimit where T: Clone { - /// Stack values. - values: Vec, - /// Stack limit (maximal stack len). +pub struct StackWithLimit { + stack: Vec, limit: usize, } -impl StackWithLimit where T: Clone { - pub fn with_limit(limit: usize) -> Self { +impl StackWithLimit { + /// Create an new StackWithLimit with `limit` max size and `initial_size` elements pre-allocated + /// `initial_size` should not be larger than `limit` + pub fn new(initial_size: usize, limit: usize) -> StackWithLimit { + debug_assert!( + limit >= initial_size, + "initial_size should not be larger than StackWithLimit limit" + ); + use std::cmp::min; StackWithLimit { - values: Vec::new(), - limit: limit + stack: Vec::with_capacity(initial_size), + limit: min(initial_size, limit), } } - pub fn is_empty(&self) -> bool { - self.values.is_empty() - } - - pub fn len(&self) -> usize { - self.values.len() - } - - pub fn top(&self) -> Result<&T, Error> { - self.values - .last() - .ok_or_else(|| Error("non-empty stack expected".into())) - } - - pub fn top_mut(&mut self) -> Result<&mut T, Error> { - self.values - .last_mut() - .ok_or_else(|| Error("non-empty stack expected".into())) - } - - pub fn get(&self, index: usize) -> Result<&T, Error> { - if index >= self.values.len() { - return Err(Error(format!("trying to get value at position {} on stack of size {}", index, self.values.len()))); + /// Create an new StackWithLimit with `limit` max size and `limit` elements pre-allocated + pub fn with_limit(limit: usize) -> StackWithLimit { + StackWithLimit { + stack: Vec::with_capacity(limit), + limit: limit, } - - Ok(self.values.get(self.values.len() - 1 - index).expect("checked couple of lines above")) } - pub fn push(&mut self, value: T) -> Result<(), Error> { - if self.values.len() >= self.limit { - return Err(Error(format!("exceeded stack limit {}", self.limit))); + /// Attempt to push value onto stack. + /// + /// # Errors + /// + /// Returns Err(StackOverflow) if stack is already full. + pub(crate) fn push(&mut self, value: T) -> Result<(), StackOverflow> { + debug_assert!( + self.stack.len() <= self.limit, + "Stack length should never be larger than stack limit." + ); + if self.stack.len() < self.limit { + self.stack.push(value); + Ok(()) + } else { + Err(StackOverflow) } - - self.values.push(value); - Ok(()) } - pub fn pop(&mut self) -> Result { - self.values - .pop() - .ok_or_else(|| Error("non-empty stack expected".into())) + pub(crate) fn pop(&mut self) -> Option { + debug_assert!( + self.stack.len() <= self.limit, + "Stack length should never be larger than stack limit." + ); + self.stack.pop() } - pub fn resize(&mut self, new_size: usize, dummy: T) { - debug_assert!(new_size <= self.values.len()); - self.values.resize(new_size, dummy); + /// Return optional reference to item in stack + /// + /// `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(&self, depth: usize) -> Option<&T> { + let offset = depth + 1; + if self.stack.len() < offset { + None + } else { + // We should be cognizant of integer underflow here. + // If offset > len(), (len() - offset) will underflow. + // In debug builds, underflow panics, but in release mode, underflow is not checked. + self.stack.get(self.stack.len() - offset) + } + } + + pub(crate) fn top(&self) -> Option<&T> { + self.stack.last() + } + + pub(crate) fn top_mut(&mut self) -> Option<&mut T> { + self.stack.last_mut() + } + + pub(crate) fn truncate(&mut self, new_size: usize) { + self.stack.truncate(new_size) + } + + pub(crate) fn len(&self) -> usize { + self.stack.len() + } + + pub(crate) fn is_empty(&self) -> bool { + self.stack.is_empty() + } + + // /// return a new empty StackWithLimit with limit equal to the amount of room + // /// this stack has available + // pub fn spare(&self) -> StackWithLimit { + // // This will be used to allocate new stacks when calling into other wasm modules + // StackWithLimit::new(0, self.limit - self.len()) + // } +} + +#[cfg(test)] +mod test { + use super::StackWithLimit; + + fn get_relative_to_top() { + let mut bstack = StackWithLimit::::with_limit(2); + bstack.push(1).unwrap(); + bstack.push(2).unwrap(); + bstack.push(3).unwrap_err(); + assert_eq!(bstack.get_relative_to_top(0), Some(&2)); + assert_eq!(bstack.get_relative_to_top(1), Some(&1)); + assert_eq!(bstack.get_relative_to_top(2), None); } } diff --git a/src/validation/func.rs b/src/validation/func.rs index 02ef454..f53bdca 100644 --- a/src/validation/func.rs +++ b/src/validation/func.rs @@ -1,14 +1,14 @@ #[allow(unused_imports)] use alloc::prelude::*; -use core::u32; -use parity_wasm::elements::{Instruction, BlockType, ValueType, TableElementType, Func, FuncBody}; use common::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; +use core::u32; +use parity_wasm::elements::{BlockType, Func, FuncBody, Instruction, TableElementType, ValueType}; use validation::context::ModuleContext; -use validation::Error; use validation::util::Locals; +use validation::Error; -use common::stack::StackWithLimit; +use common::stack::{StackOverflow, StackWithLimit}; use isa; /// Maximum number of entries in value stack per function. @@ -39,13 +39,9 @@ enum BlockFrameType { /// Usual block frame. /// /// Can be used for an implicit function block. - Block { - end_label: LabelId, - }, + Block { end_label: LabelId }, /// Loop frame (branching to the beginning of block). - Loop { - header: LabelId, - }, + Loop { header: LabelId }, /// True-subblock of if expression. IfTrue { /// If jump happens inside the if-true block then control will @@ -58,9 +54,7 @@ enum BlockFrameType { if_not: LabelId, }, /// False-subblock of if expression. - IfFalse { - end_label: LabelId, - } + IfFalse { end_label: LabelId }, } impl BlockFrameType { @@ -164,11 +158,7 @@ enum Outcome { pub struct FunctionReader; impl FunctionReader { - pub fn read_function( - module: &ModuleContext, - func: &Func, - body: &FuncBody, - ) -> Result { + pub fn read_function(module: &ModuleContext, func: &Func, body: &FuncBody) -> Result { let (params, result_ty) = module.require_function_type(func.type_ref())?; let ins_size_estimate = body.code().elements().len(); @@ -183,9 +173,7 @@ impl FunctionReader { let end_label = context.sink.new_label(); push_label( - BlockFrameType::Block { - end_label, - }, + BlockFrameType::Block { end_label }, result_ty, context.position, &context.value_stack, @@ -207,15 +195,16 @@ impl FunctionReader { loop { let instruction = &body[context.position]; - let outcome = FunctionReader::read_instruction(context, instruction) - .map_err(|err| Error(format!("At instruction {:?}(@{}): {}", instruction, context.position, err)))?; + let outcome = FunctionReader::read_instruction(context, instruction).map_err(|err| { + Error(format!( + "At instruction {:?}(@{}): {}", + instruction, context.position, err + )) + })?; match outcome { Outcome::NextInstruction => (), - Outcome::Unreachable => make_top_frame_polymorphic( - &mut context.value_stack, - &mut context.frame_stack - ), + Outcome::Unreachable => make_top_frame_polymorphic(&mut context.value_stack, &mut context.frame_stack), } context.position += 1; @@ -229,7 +218,7 @@ impl FunctionReader { use self::Instruction::*; match *instruction { // Nop instruction doesn't do anything. It is safe to just skip it. - Nop => {}, + Nop => {} Unreachable => { context.sink.emit(isa::Instruction::Unreachable); @@ -239,9 +228,7 @@ impl FunctionReader { Block(block_type) => { let end_label = context.sink.new_label(); push_label( - BlockFrameType::Block { - end_label - }, + BlockFrameType::Block { end_label }, block_type, context.position, &context.value_stack, @@ -254,9 +241,7 @@ impl FunctionReader { context.sink.resolve_label(header); push_label( - BlockFrameType::Loop { - header, - }, + BlockFrameType::Loop { header }, block_type, context.position, &context.value_stack, @@ -271,10 +256,7 @@ impl FunctionReader { pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; push_label( - BlockFrameType::IfTrue { - if_not, - end_label, - }, + BlockFrameType::IfTrue { if_not, end_label }, block_type, context.position, &context.value_stack, @@ -283,14 +265,15 @@ impl FunctionReader { context.sink.emit_br_eqz(Target { label: if_not, - drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, }, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, }); } Else => { let (block_type, if_not, end_label) = { - let top_frame = top_label( - &context.frame_stack, - ); + let top_frame = top_label(&context.frame_stack); let (if_not, end_label) = match top_frame.frame_type { BlockFrameType::IfTrue { if_not, end_label } => (if_not, end_label), @@ -303,7 +286,10 @@ impl FunctionReader { // to the "end_label" (it will be resolved at End). context.sink.emit_br(Target { label: end_label, - drop_keep: isa::DropKeep { drop: 0, keep: isa::Keep::None, }, + drop_keep: isa::DropKeep { + drop: 0, + keep: isa::Keep::None, + }, }); // Resolve `if_not` to here so when if condition is unsatisfied control flow @@ -312,14 +298,9 @@ impl FunctionReader { // Then, we pop the current label. It discards all values that pushed in the current // frame. - pop_label( - &mut context.value_stack, - &mut context.frame_stack - )?; + pop_label(&mut context.value_stack, &mut context.frame_stack)?; push_label( - BlockFrameType::IfFalse { - end_label, - }, + BlockFrameType::IfFalse { end_label }, block_type, context.position, &context.value_stack, @@ -335,14 +316,10 @@ impl FunctionReader { if let BlockFrameType::IfTrue { if_not, .. } = frame_type { // A `if` without an `else` can't return a result. if block_type != BlockType::NoResult { - return Err( - Error( - format!( - "If block without else required to have NoResult block type. But it has {:?} type", - block_type - ) - ) - ); + return Err(Error(format!( + "If block without else required to have NoResult block type. But it has {:?} type", + block_type + ))); } // Resolve `if_not` label. If the `if's` condition doesn't hold the control will jump @@ -362,19 +339,11 @@ impl FunctionReader { // Check the return type. if let BlockType::Value(value_type) = context.return_type()? { - tee_value( - &mut context.value_stack, - &context.frame_stack, - value_type.into() - )?; + tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; } // Emit the return instruction. - let drop_keep = drop_keep_return( - &context.locals, - &context.value_stack, - &context.frame_stack, - ); + let drop_keep = drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack); context.sink.emit(isa::Instruction::Return(drop_keep)); } @@ -388,11 +357,7 @@ impl FunctionReader { Br(depth) => { Validator::validate_br(context, depth)?; - let target = require_target( - depth, - &context.value_stack, - &context.frame_stack, - ); + let target = require_target(depth, &context.value_stack, &context.frame_stack); context.sink.emit_br(target); return Ok(Outcome::Unreachable); @@ -400,11 +365,7 @@ impl FunctionReader { BrIf(depth) => { Validator::validate_br_if(context, depth)?; - let target = require_target( - depth, - &context.value_stack, - &context.frame_stack, - ); + let target = require_target(depth, &context.value_stack, &context.frame_stack); context.sink.emit_br_nez(target); } BrTable(ref table, default) => { @@ -412,18 +373,10 @@ impl FunctionReader { let mut targets = Vec::new(); for depth in table.iter() { - let target = require_target( - *depth, - &context.value_stack, - &context.frame_stack, - ); + let target = require_target(*depth, &context.value_stack, &context.frame_stack); targets.push(target); } - let default_target = require_target( - default, - &context.value_stack, - &context.frame_stack, - ); + let default_target = require_target(default, &context.value_stack, &context.frame_stack); context.sink.emit_br_table(&targets, default_target); return Ok(Outcome::Unreachable); @@ -433,11 +386,7 @@ impl FunctionReader { tee_value(&mut context.value_stack, &context.frame_stack, value_type.into())?; } - let drop_keep = drop_keep_return( - &context.locals, - &context.value_stack, - &context.frame_stack, - ); + let drop_keep = drop_keep_return(&context.locals, &context.value_stack, &context.frame_stack); context.sink.emit(isa::Instruction::Return(drop_keep)); return Ok(Outcome::Unreachable); @@ -464,37 +413,19 @@ impl FunctionReader { GetLocal(index) => { // We need to calculate relative depth before validation since // it will change the value stack size. - let depth = relative_local_depth( - index, - &context.locals, - &context.value_stack, - )?; + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; Validator::validate_get_local(context, index)?; - context.sink.emit( - isa::Instruction::GetLocal(depth), - ); + context.sink.emit(isa::Instruction::GetLocal(depth)); } SetLocal(index) => { Validator::validate_set_local(context, index)?; - let depth = relative_local_depth( - index, - &context.locals, - &context.value_stack, - )?; - context.sink.emit( - isa::Instruction::SetLocal(depth), - ); + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + context.sink.emit(isa::Instruction::SetLocal(depth)); } TeeLocal(index) => { Validator::validate_tee_local(context, index)?; - let depth = relative_local_depth( - index, - &context.locals, - &context.value_stack, - )?; - context.sink.emit( - isa::Instruction::TeeLocal(depth), - ); + let depth = relative_local_depth(index, &context.locals, &context.value_stack)?; + context.sink.emit(isa::Instruction::TeeLocal(depth)); } GetGlobal(index) => { Validator::validate_get_global(context, index)?; @@ -1167,7 +1098,11 @@ impl Validator { Ok(()) } - fn validate_cvtop(context: &mut FunctionValidationContext, value_type1: ValueType, value_type2: ValueType) -> Result<(), Error> { + fn validate_cvtop( + context: &mut FunctionValidationContext, + value_type1: ValueType, + value_type2: ValueType, + ) -> Result<(), Error> { pop_value(&mut context.value_stack, &context.frame_stack, value_type1.into())?; push_value(&mut context.value_stack, value_type2.into())?; Ok(()) @@ -1195,8 +1130,11 @@ impl Validator { fn validate_set_local(context: &mut FunctionValidationContext, index: u32) -> Result<(), Error> { let local_type = require_local(&context.locals, index)?; let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?; - if StackValueType::from(local_type) != value_type { - return Err(Error(format!("Trying to update local {} of type {:?} with value of type {:?}", index, local_type, value_type))); + if StackValueType::from(local_type) != value_type { + return Err(Error(format!( + "Trying to update local {} of type {:?} with value of type {:?}", + index, local_type, value_type + ))); } Ok(()) } @@ -1223,14 +1161,25 @@ impl Validator { }; let value_type = pop_value(&mut context.value_stack, &context.frame_stack, StackValueType::Any)?; if global_type != value_type { - return Err(Error(format!("Trying to update global {} of type {:?} with value of type {:?}", index, global_type, value_type))); + return Err(Error(format!( + "Trying to update global {} of type {:?} with value of type {:?}", + index, global_type, value_type + ))); } Ok(()) } - fn validate_load(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<(), Error> { + fn validate_load( + context: &mut FunctionValidationContext, + align: u32, + max_align: u32, + value_type: ValueType, + ) -> Result<(), Error> { if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { - return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); + return Err(Error(format!( + "Too large memory alignment 2^{} (expected at most {})", + align, max_align + ))); } pop_value(&mut context.value_stack, &context.frame_stack, ValueType::I32.into())?; @@ -1239,9 +1188,17 @@ impl Validator { Ok(()) } - fn validate_store(context: &mut FunctionValidationContext, align: u32, max_align: u32, value_type: ValueType) -> Result<(), Error> { + fn validate_store( + context: &mut FunctionValidationContext, + align: u32, + max_align: u32, + value_type: ValueType, + ) -> Result<(), Error> { if 1u32.checked_shl(align).unwrap_or(u32::MAX) > max_align { - return Err(Error(format!("Too large memory alignment 2^{} (expected at most {})", align, max_align))); + return Err(Error(format!( + "Too large memory alignment 2^{} (expected at most {})", + align, max_align + ))); } context.module.require_memory(DEFAULT_MEMORY_INDEX)?; @@ -1295,15 +1252,10 @@ impl Validator { BlockType::NoResult }; if required_block_type != label_block_type { - return Err( - Error( - format!( - "Labels in br_table points to block of different types: {:?} and {:?}", - required_block_type, - label_block.block_type - ) - ) - ); + return Err(Error(format!( + "Labels in br_table points to block of different types: {:?} and {:?}", + required_block_type, label_block.block_type + ))); } } required_block_type @@ -1416,15 +1368,22 @@ fn make_top_frame_polymorphic( value_stack: &mut StackWithLimit, frame_stack: &mut StackWithLimit, ) { - 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); + let frame = frame_stack + .top_mut() + .expect("make_top_frame_polymorphic is called with empty frame stack"); + + // 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; } -fn push_value( - value_stack: &mut StackWithLimit, - value_type: StackValueType, -) -> Result<(), Error> { +fn push_value(value_stack: &mut StackWithLimit, value_type: StackValueType) -> Result<(), Error> { Ok(value_stack.push(value_type.into())?) } @@ -1442,19 +1401,16 @@ fn pop_value( let actual_value = if stack_is_empty && is_stack_polymorphic { StackValueType::Any } else { - let value_stack_min = frame_stack - .top() - .expect("at least 1 topmost block") - .value_stack_len; + let value_stack_min = frame_stack.top().expect("at least 1 topmost block").value_stack_len; 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 => { - Ok(actual_value) - } + StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(actual_value), StackValueType::Any => Ok(actual_value), stack_value_type @ _ => Err(Error(format!( "Expected value of type {:?} on top of stack. Got {:?}", @@ -1497,7 +1453,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) => { @@ -1505,7 +1464,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 {}", @@ -1518,15 +1479,15 @@ fn pop_label( } fn top_label(frame_stack: &StackWithLimit) -> &BlockFrame { - frame_stack.top() + frame_stack + .top() .expect("this function can't be called with empty frame stack") } -fn require_label( - depth: u32, - frame_stack: &StackWithLimit, -) -> Result<&BlockFrame, Error> { - Ok(frame_stack.get(depth as usize)?) +fn require_label(depth: u32, frame_stack: &StackWithLimit) -> Result<&BlockFrame, Error> { + Ok(frame_stack + .get_relative_to_top(depth as usize) + .ok_or_else(|| Error("non-empty stack expected".into()))?) } fn require_target( @@ -1534,10 +1495,8 @@ fn require_target( value_stack: &StackWithLimit, frame_stack: &StackWithLimit, ) -> Target { - let is_stack_polymorphic = top_label(frame_stack) - .polymorphic_stack; - let frame = - require_label(depth, frame_stack).expect("require_target called with a bogus depth"); + let is_stack_polymorphic = top_label(frame_stack).polymorphic_stack; + let frame = require_label(depth, frame_stack).expect("require_target called with a bogus depth"); // Find out how many values we need to keep (copy to the new stack location after the drop). let keep: isa::Keep = match (frame.frame_type, frame.block_type) { @@ -1601,20 +1560,14 @@ fn require_local(locals: &Locals, idx: u32) -> Result { } /// See stack layout definition in mod isa. -fn relative_local_depth( - idx: u32, - locals: &Locals, - value_stack: &StackWithLimit -) -> Result { +fn relative_local_depth(idx: u32, locals: &Locals, value_stack: &StackWithLimit) -> Result { let value_stack_height = value_stack.len() as u32; let locals_and_params_count = locals.count(); let depth = value_stack_height .checked_add(locals_and_params_count) .and_then(|x| x.checked_sub(idx)) - .ok_or_else(|| - Error(String::from("Locals range not in 32-bit range")) - )?; + .ok_or_else(|| Error(String::from("Locals range not in 32-bit range")))?; Ok(depth) } @@ -1659,8 +1612,7 @@ impl Sink { match self.labels[label.0] { (Label::Resolved(dst_pc), _) => dst_pc, (Label::NotResolved, ref mut unresolved) => { - unresolved - .push(reloc_creator()); + unresolved.push(reloc_creator()); u32::max_value() } } @@ -1671,10 +1623,7 @@ impl Sink { } fn emit_br(&mut self, target: Target) { - let Target { - label, - drop_keep, - } = target; + let Target { label, drop_keep } = target; let pc = self.cur_pc(); let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); self.ins.push(isa::Instruction::Br(isa::Target { @@ -1684,10 +1633,7 @@ impl Sink { } fn emit_br_eqz(&mut self, target: Target) { - let Target { - label, - drop_keep, - } = target; + let Target { label, drop_keep } = target; let pc = self.cur_pc(); let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); self.ins.push(isa::Instruction::BrIfEqz(isa::Target { @@ -1697,10 +1643,7 @@ impl Sink { } fn emit_br_nez(&mut self, target: Target) { - let Target { - label, - drop_keep, - } = target; + let Target { label, drop_keep } = target; let pc = self.cur_pc(); let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::Br { pc }); self.ins.push(isa::Instruction::BrIfNez(isa::Target { @@ -1716,24 +1659,18 @@ impl Sink { let mut isa_targets = Vec::new(); for (idx, &Target { label, drop_keep }) in targets.iter().chain(iter::once(&default)).enumerate() { let dst_pc = self.pc_or_placeholder(label, || isa::Reloc::BrTable { pc, idx }); - isa_targets.push( - isa::Target { - dst_pc, - drop_keep: drop_keep.into(), - }, - ); + isa_targets.push(isa::Target { + dst_pc, + drop_keep: drop_keep.into(), + }); } - self.ins.push(isa::Instruction::BrTable( - isa_targets.into_boxed_slice(), - )); + self.ins.push(isa::Instruction::BrTable(isa_targets.into_boxed_slice())); } /// Create a new unresolved label. fn new_label(&mut self) -> LabelId { let label_idx = self.labels.len(); - self.labels.push( - (Label::NotResolved, Vec::new()), - ); + self.labels.push((Label::NotResolved, Vec::new())); LabelId(label_idx) } @@ -1762,14 +1699,16 @@ impl Sink { /// Consume this Sink and returns isa::Instructions. fn into_inner(self) -> isa::Instructions { // At this moment all labels should be resolved. - assert!({ - self.labels.iter().all(|(state, unresolved)| - match (state, unresolved) { + assert!( + { + self.labels.iter().all(|(state, unresolved)| match (state, unresolved) { (Label::Resolved(_), unresolved) if unresolved.is_empty() => true, _ => false, - } - ) - }, "there are unresolved labels left: {:?}", self.labels); + }) + }, + "there are unresolved labels left: {:?}", + self.labels + ); self.ins } } diff --git a/src/validation/mod.rs b/src/validation/mod.rs index fdb2d11..dd642b0 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -1,23 +1,23 @@ #[allow(unused_imports)] use alloc::prelude::*; +use core::fmt; #[cfg(feature = "std")] use std::error; -use core::fmt; -#[cfg(feature = "std")] -use std::collections::HashSet; #[cfg(not(feature = "std"))] use hashmap_core::HashSet; +#[cfg(feature = "std")] +use std::collections::HashSet; -use parity_wasm::elements::{ - BlockType, External, GlobalEntry, GlobalType, Internal, MemoryType, Module, Instruction, - ResizableLimits, TableType, ValueType, InitExpr, Type, -}; -use common::stack; use self::context::ModuleContextBuilder; use self::func::FunctionReader; -use memory_units::Pages; +use common::stack; use isa; +use memory_units::Pages; +use parity_wasm::elements::{ + BlockType, External, GlobalEntry, GlobalType, InitExpr, Instruction, Internal, MemoryType, Module, ResizableLimits, + TableType, Type, ValueType, +}; mod context; mod func; @@ -42,9 +42,9 @@ impl error::Error for Error { } } -impl From for Error { - fn from(e: stack::Error) -> Error { - Error(format!("Stack: {}", e)) +impl From for Error { + fn from(_: stack::StackOverflow) -> Error { + Error("Stack: exeeded stack limit".into()) } } @@ -158,7 +158,8 @@ pub fn deny_floating_point(module: &Module) -> Result<(), Error> { if let Some(typ) = types.get(sig.type_ref() as usize) { match *typ { Type::Function(ref func) => { - if func.params() + if func + .params() .iter() .chain(func.return_type().as_ref()) .any(|&typ| typ == ValueType::F32 || typ == ValueType::F64) @@ -189,16 +190,11 @@ pub fn validate_module(module: Module) -> Result { .map(|&Type::Function(ref ty)| ty) .cloned() .collect() - }) - .unwrap_or_default(), + }).unwrap_or_default(), ); // Fill elements with imported values. - for import_entry in module - .import_section() - .map(|i| i.entries()) - .unwrap_or_default() - { + for import_entry in module.import_section().map(|i| i.entries()).unwrap_or_default() { match *import_entry.external() { External::Function(idx) => context_builder.push_func_type_index(idx), External::Table(ref table) => context_builder.push_table(table.clone()), @@ -237,41 +233,32 @@ pub fn validate_module(module: Module) -> Result { let context = context_builder.build(); - let function_section_len = module - .function_section() - .map(|s| s.entries().len()) - .unwrap_or(0); + let function_section_len = module.function_section().map(|s| s.entries().len()).unwrap_or(0); let code_section_len = module.code_section().map(|s| s.bodies().len()).unwrap_or(0); if function_section_len != code_section_len { return Err(Error(format!( "length of function section is {}, while len of code section is {}", - function_section_len, - code_section_len + function_section_len, code_section_len ))); } // validate every function body in user modules if function_section_len != 0 { // tests use invalid code - let function_section = module.function_section().expect( - "function_section_len != 0; qed", - ); - let code_section = module.code_section().expect( - "function_section_len != 0; function_section_len == code_section_len; qed", - ); + let function_section = module.function_section().expect("function_section_len != 0; qed"); + let code_section = module + .code_section() + .expect("function_section_len != 0; function_section_len == code_section_len; qed"); // check every function body for (index, function) in function_section.entries().iter().enumerate() { - let function_body = code_section.bodies().get(index as usize).ok_or( - Error(format!( - "Missing body for function {}", - index - )), - )?; - let code = FunctionReader::read_function(&context, function, function_body) - .map_err(|e| { - let Error(ref msg) = e; - Error(format!("Function #{} reading/validation error: {}", index, msg)) - })?; + let function_body = code_section + .bodies() + .get(index as usize) + .ok_or(Error(format!("Missing body for function {}", index)))?; + let code = FunctionReader::read_function(&context, function, function_body).map_err(|e| { + let Error(ref msg) = e; + Error(format!("Function #{} reading/validation error: {}", index, msg)) + })?; code_map.push(code); } } @@ -280,9 +267,7 @@ pub fn validate_module(module: Module) -> Result { if let Some(start_fn_idx) = module.start_section() { let (params, return_ty) = context.require_function(start_fn_idx)?; if return_ty != BlockType::NoResult || params.len() != 0 { - return Err(Error( - "start function expected to have type [] -> []".into(), - )); + return Err(Error("start function expected to have type [] -> []".into())); } } @@ -293,9 +278,7 @@ pub fn validate_module(module: Module) -> Result { // HashSet::insert returns false if item already in set. let duplicate = export_names.insert(export.field()) == false; if duplicate { - return Err(Error( - format!("duplicate export {}", export.field()), - )); + return Err(Error(format!("duplicate export {}", export.field()))); } match *export.internal() { Internal::Function(function_index) => { @@ -323,10 +306,7 @@ pub fn validate_module(module: Module) -> Result { } External::Global(ref global_type) => { if global_type.is_mutable() { - return Err(Error(format!( - "trying to import mutable global {}", - import.field() - ))); + return Err(Error(format!("trying to import mutable global {}", import.field()))); } } External::Memory(ref memory_type) => { @@ -382,10 +362,7 @@ pub fn validate_module(module: Module) -> Result { } } - Ok(ValidatedModule { - module, - code_map, - }) + Ok(ValidatedModule { module, code_map }) } fn validate_limits(limits: &ResizableLimits) -> Result<(), Error> { @@ -413,45 +390,37 @@ fn validate_table_type(table_type: &TableType) -> Result<(), Error> { fn validate_global_entry(global_entry: &GlobalEntry, globals: &[GlobalType]) -> Result<(), Error> { let init = global_entry.init_expr(); - let init_expr_ty = expr_const_type(init, globals)?; - if init_expr_ty != global_entry.global_type().content_type() { - return Err(Error(format!( - "Trying to initialize variable of type {:?} with value of type {:?}", - global_entry.global_type().content_type(), - init_expr_ty - ))); - } - Ok(()) + let init_expr_ty = expr_const_type(init, globals)?; + if init_expr_ty != global_entry.global_type().content_type() { + return Err(Error(format!( + "Trying to initialize variable of type {:?} with value of type {:?}", + global_entry.global_type().content_type(), + init_expr_ty + ))); + } + Ok(()) } /// Returns type of this constant expression. fn expr_const_type(init_expr: &InitExpr, globals: &[GlobalType]) -> Result { let code = init_expr.code(); if code.len() != 2 { - return Err(Error( - "Init expression should always be with length 2".into(), - )); + return Err(Error("Init expression should always be with length 2".into())); } let expr_ty: ValueType = match code[0] { Instruction::I32Const(_) => ValueType::I32, Instruction::I64Const(_) => ValueType::I64, Instruction::F32Const(_) => ValueType::F32, Instruction::F64Const(_) => ValueType::F64, - Instruction::GetGlobal(idx) => { - match globals.get(idx as usize) { - Some(target_global) => { - if target_global.is_mutable() { - return Err(Error(format!("Global {} is mutable", idx))); - } - target_global.content_type() - } - None => { - return Err(Error( - format!("Global {} doesn't exists or not yet defined", idx), - )) + Instruction::GetGlobal(idx) => match globals.get(idx as usize) { + Some(target_global) => { + if target_global.is_mutable() { + return Err(Error(format!("Global {} is mutable", idx))); } + target_global.content_type() } - } + None => return Err(Error(format!("Global {} doesn't exists or not yet defined", idx))), + }, _ => return Err(Error("Non constant opcode in init expr".into())), }; if code[1] != Instruction::End {