This commit is contained in:
Sergey Pepyakin
2017-12-05 12:26:42 +01:00
parent 8afb7b6450
commit 71b8b933bf
3 changed files with 65 additions and 31 deletions

View File

@ -21,7 +21,7 @@ const DEFAULT_VALUE_STACK_LIMIT: usize = 16384;
const DEFAULT_FRAME_STACK_LIMIT: usize = 1024; const DEFAULT_FRAME_STACK_LIMIT: usize = 1024;
/// Function validation context. /// Function validation context.
pub struct FunctionValidationContext<'a> { struct FunctionValidationContext<'a> {
/// Wasm module /// Wasm module
module: &'a ModuleContext, module: &'a ModuleContext,
/// Current instruction position. /// Current instruction position.
@ -40,7 +40,7 @@ pub struct FunctionValidationContext<'a> {
/// Value type on the stack. /// Value type on the stack.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum StackValueType { enum StackValueType {
/// Any value type. /// Any value type.
Any, Any,
/// Any number of any values of any type. /// Any number of any values of any type.
@ -54,7 +54,7 @@ pub struct Validator;
/// Instruction outcome. /// Instruction outcome.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum InstructionOutcome { enum InstructionOutcome {
/// Continue with next instruction. /// Continue with next instruction.
ValidateNextInstruction, ValidateNextInstruction,
/// Unreachable instruction reached. /// Unreachable instruction reached.
@ -590,7 +590,7 @@ impl Validator {
} }
impl<'a> FunctionValidationContext<'a> { impl<'a> FunctionValidationContext<'a> {
pub fn new( fn new(
module: &'a ModuleContext, module: &'a ModuleContext,
locals: &'a [ValueType], locals: &'a [ValueType],
value_stack_limit: usize, value_stack_limit: usize,
@ -608,11 +608,11 @@ impl<'a> FunctionValidationContext<'a> {
} }
} }
pub fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> { fn push_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
Ok(self.value_stack.push(value_type.into())?) Ok(self.value_stack.push(value_type.into())?)
} }
pub fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> { fn pop_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
self.check_stack_access()?; self.check_stack_access()?;
match self.value_stack.pop()? { match self.value_stack.pop()? {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()),
@ -625,7 +625,7 @@ impl<'a> FunctionValidationContext<'a> {
} }
} }
pub fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> { fn tee_value(&mut self, value_type: StackValueType) -> Result<(), Error> {
self.check_stack_access()?; self.check_stack_access()?;
match *self.value_stack.top()? { match *self.value_stack.top()? {
StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()), StackValueType::Specific(stack_value_type) if stack_value_type == value_type => Ok(()),
@ -634,7 +634,7 @@ impl<'a> FunctionValidationContext<'a> {
} }
} }
pub fn pop_any_value(&mut self) -> Result<StackValueType, Error> { fn pop_any_value(&mut self) -> Result<StackValueType, Error> {
self.check_stack_access()?; self.check_stack_access()?;
match self.value_stack.pop()? { match self.value_stack.pop()? {
StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)), StackValueType::Specific(stack_value_type) => Ok(StackValueType::Specific(stack_value_type)),
@ -646,20 +646,20 @@ impl<'a> FunctionValidationContext<'a> {
} }
} }
pub fn tee_any_value(&mut self) -> Result<StackValueType, Error> { fn tee_any_value(&mut self) -> Result<StackValueType, Error> {
self.check_stack_access()?; self.check_stack_access()?;
Ok(self.value_stack.top().map(Clone::clone)?) Ok(self.value_stack.top().map(Clone::clone)?)
} }
pub fn unreachable(&mut self) -> Result<(), Error> { fn unreachable(&mut self) -> Result<(), Error> {
Ok(self.value_stack.push(StackValueType::AnyUnlimited)?) Ok(self.value_stack.push(StackValueType::AnyUnlimited)?)
} }
pub fn top_label(&self) -> Result<&BlockFrame, Error> { fn top_label(&self) -> Result<&BlockFrame, Error> {
Ok(self.frame_stack.top()?) Ok(self.frame_stack.top()?)
} }
pub fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> { fn push_label(&mut self, frame_type: BlockFrameType, block_type: BlockType) -> Result<(), Error> {
Ok(self.frame_stack.push(BlockFrame { Ok(self.frame_stack.push(BlockFrame {
frame_type: frame_type, frame_type: frame_type,
block_type: block_type, block_type: block_type,
@ -670,7 +670,7 @@ impl<'a> FunctionValidationContext<'a> {
})?) })?)
} }
pub fn pop_label(&mut self) -> Result<InstructionOutcome, Error> { fn pop_label(&mut self) -> Result<InstructionOutcome, Error> {
let frame = self.frame_stack.pop()?; let frame = self.frame_stack.pop()?;
let actual_value_type = if self.value_stack.len() > frame.value_stack_len { let actual_value_type = if self.value_stack.len() > frame.value_stack_len {
Some(self.value_stack.pop()?) Some(self.value_stack.pop()?)
@ -694,22 +694,22 @@ impl<'a> FunctionValidationContext<'a> {
Ok(InstructionOutcome::ValidateNextInstruction) Ok(InstructionOutcome::ValidateNextInstruction)
} }
pub fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> { fn require_label(&self, idx: u32) -> Result<&BlockFrame, Error> {
Ok(self.frame_stack.get(idx as usize)?) Ok(self.frame_stack.get(idx as usize)?)
} }
pub fn return_type(&self) -> Result<BlockType, Error> { fn return_type(&self) -> Result<BlockType, Error> {
self.return_type.ok_or(Error("Trying to return from expression".into())) self.return_type.ok_or(Error("Trying to return from expression".into()))
} }
pub fn require_local(&self, idx: u32) -> Result<StackValueType, Error> { fn require_local(&self, idx: u32) -> Result<StackValueType, Error> {
self.locals.get(idx as usize) self.locals.get(idx as usize)
.cloned() .cloned()
.map(Into::into) .map(Into::into)
.ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len()))) .ok_or(Error(format!("Trying to access local with index {} when there are only {} locals", idx, self.locals.len())))
} }
pub fn function_labels(self) -> HashMap<usize, usize> { fn function_labels(self) -> HashMap<usize, usize> {
self.labels self.labels
} }
@ -724,21 +724,21 @@ impl<'a> FunctionValidationContext<'a> {
} }
impl StackValueType { impl StackValueType {
pub fn is_any(&self) -> bool { fn is_any(&self) -> bool {
match self { match self {
&StackValueType::Any => true, &StackValueType::Any => true,
_ => false, _ => false,
} }
} }
pub fn is_any_unlimited(&self) -> bool { fn is_any_unlimited(&self) -> bool {
match self { match self {
&StackValueType::AnyUnlimited => true, &StackValueType::AnyUnlimited => true,
_ => false, _ => false,
} }
} }
pub fn value_type(&self) -> ValueType { fn value_type(&self) -> ValueType {
match self { match self {
&StackValueType::Any | &StackValueType::AnyUnlimited => unreachable!("must be checked by caller"), &StackValueType::Any | &StackValueType::AnyUnlimited => unreachable!("must be checked by caller"),
&StackValueType::Specific(value_type) => value_type, &StackValueType::Specific(value_type) => value_type,

View File

@ -6,7 +6,7 @@ use elements::{BlockType, External, FunctionType, GlobalEntry, GlobalType, Inter
Module, Opcode, ResizableLimits, TableType, Type, ValueType}; Module, Opcode, ResizableLimits, TableType, Type, ValueType};
use common::stack; use common::stack;
use self::context::ModuleContext; use self::context::ModuleContext;
use self::func::{FunctionValidationContext, Validator}; use self::func::Validator;
pub use self::module::ValidatedModule; pub use self::module::ValidatedModule;

View File

@ -196,6 +196,40 @@ fn global_init_misc() {
assert!(validate_module(&m).is_err()); assert!(validate_module(&m).is_err());
} }
#[test]
fn module_limits_validity() {
// module cannot contain more than 1 memory atm.
let m = module()
.with_import(
ImportEntry::new(
"core".into(),
"memory".into(),
External::Memory(MemoryType::new(10, None))
)
)
.memory()
.with_min(10)
.build()
.build();
assert!(validate_module(&m).is_err());
// module cannot contain more than 1 table atm.
let m = module()
.with_import(
ImportEntry::new(
"core".into(),
"table".into(),
External::Table(TableType::new(10, None))
)
)
.table()
.with_min(10)
.build()
.build();
assert!(validate_module(&m).is_err());
}
// TODO: pepyakin
// #[test] // #[test]
// fn if_else_with_return_type_validation() { // fn if_else_with_return_type_validation() {
// let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap(); // let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap();