use ModuleInstance methods for validation

This commit is contained in:
Svyatoslav Nikolsky
2017-06-23 10:39:31 +03:00
parent c944b4c91e
commit 8209ff7d6d
4 changed files with 64 additions and 122 deletions

View File

@ -39,14 +39,8 @@ pub enum ExportEntryType {
Global(VariableType), Global(VariableType),
} }
pub struct InstantiationParams {
}
/// Module instance API. /// Module instance API.
pub trait ModuleInstanceInterface { pub trait ModuleInstanceInterface {
/// Run instantiation-time procedures (validation and start function [if any] call). Module is not completely validated until this call.
//fn instantiate<'a>(&self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error>;
/// Execute function with the given index. /// Execute function with the given index.
fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>; fn execute_index(&self, index: u32, params: ExecutionParams) -> Result<Option<RuntimeValue>, Error>;
/// Execute function with the given export name. /// Execute function with the given export name.
@ -214,6 +208,7 @@ impl ModuleInstance {
}) })
} }
/// Run instantiation-time procedures (validation). Module is not completely validated until this call.
pub fn instantiate<'a>(&mut self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> { pub fn instantiate<'a>(&mut self, is_user_module: bool, externals: Option<&'a HashMap<String, Arc<ModuleInstanceInterface + 'a>>>) -> Result<(), Error> {
// validate start section // validate start section
if let Some(start_function) = self.module.start_section() { if let Some(start_function) = self.module.start_section() {
@ -319,24 +314,27 @@ impl ModuleInstance {
let function_body = code_section.bodies().get(index as usize).ok_or(Error::Validation(format!("Missing body for function {}", index)))?; let function_body = code_section.bodies().get(index as usize).ok_or(Error::Validation(format!("Missing body for function {}", index)))?;
let mut locals = function_type.params().to_vec(); let mut locals = function_type.params().to_vec();
locals.extend(function_body.locals().iter().flat_map(|l| repeat(l.value_type()).take(l.count() as usize))); locals.extend(function_body.locals().iter().flat_map(|l| repeat(l.value_type()).take(l.count() as usize)));
let mut context = FunctionValidationContext::new(
&self.module,
&self.imports,
&locals,
DEFAULT_VALUE_STACK_LIMIT,
DEFAULT_FRAME_STACK_LIMIT,
&function_type);
let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult); let function_labels = {
Validator::validate_function(&mut context, block_type, function_body.code().elements()) let mut context = FunctionValidationContext::new(
.map_err(|e| { self,
if let Error::Validation(msg) = e { &locals,
Error::Validation(format!("Function #{} validation error: {}", index, msg)) DEFAULT_VALUE_STACK_LIMIT,
} else { DEFAULT_FRAME_STACK_LIMIT,
e &function_type);
}
})?; let block_type = function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult);
self.functions_labels.insert(index as u32, context.function_labels()); Validator::validate_function(&mut context, block_type, function_body.code().elements())
.map_err(|e| {
if let Error::Validation(msg) = e {
Error::Validation(format!("Function #{} validation error: {}", index, msg))
} else {
e
}
})?;
context.function_labels()
};
self.functions_labels.insert(index as u32, function_labels);
} }
} }
@ -369,6 +367,7 @@ impl ModuleInstance {
Ok(()) Ok(())
} }
/// Run start function [if any].
pub fn run_start_function(&self) -> Result<(), Error> { pub fn run_start_function(&self) -> Result<(), Error> {
// execute start function (if any) // execute start function (if any)
if let Some(start_function) = self.module.start_section() { if let Some(start_function) = self.module.start_section() {

View File

@ -29,6 +29,11 @@ impl TableInstance {
})) }))
} }
/// Get variable type for this table.
pub fn variable_type(&self) -> VariableType {
self.variable_type
}
/// Get the specific value in the table /// Get the specific value in the table
pub fn get(&self, offset: u32) -> Result<RuntimeValue, Error> { pub fn get(&self, offset: u32) -> Result<RuntimeValue, Error> {
let buffer = self.buffer.read(); let buffer = self.buffer.read();

View File

@ -6,9 +6,8 @@ use elements::{ExportEntry, Internal, ImportEntry, External, GlobalEntry, Global
InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType}; InitExpr, ValueType, BlockType, Opcodes, Opcode, FunctionType};
use interpreter::Error; use interpreter::Error;
use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor}; use interpreter::env_native::{env_native_module, UserFunction, UserFunctions, UserFunctionExecutor, UserFunctionDescriptor};
use interpreter::imports::ModuleImports;
use interpreter::memory::MemoryInstance; use interpreter::memory::MemoryInstance;
use interpreter::module::{ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType}; use interpreter::module::{ModuleInstance, ModuleInstanceInterface, CallerContext, ItemIndex, ExecutionParams, ExportEntryType};
use interpreter::program::ProgramInstance; use interpreter::program::ProgramInstance;
use interpreter::validator::{FunctionValidationContext, Validator}; use interpreter::validator::{FunctionValidationContext, Validator};
use interpreter::value::RuntimeValue; use interpreter::value::RuntimeValue;
@ -226,9 +225,8 @@ fn env_native_export_entry_type_check() {
#[test] #[test]
fn if_else_with_return_type_validation() { fn if_else_with_return_type_validation() {
let module = module().build(); let module_instance = ModuleInstance::new(Weak::default(), "test".into(), module().build()).unwrap();
let imports = ModuleImports::new(Weak::default(), None); let mut context = FunctionValidationContext::new(&module_instance, &[], 1024, 1024, &FunctionType::default());
let mut context = FunctionValidationContext::new(&module, &imports, &[], 1024, 1024, &FunctionType::default());
Validator::validate_function(&mut context, BlockType::NoResult, &[ Validator::validate_function(&mut context, BlockType::NoResult, &[
Opcode::I32Const(1), Opcode::I32Const(1),

View File

@ -1,10 +1,9 @@
use std::u32; use std::u32;
use std::collections::{HashMap, VecDeque}; use std::collections::HashMap;
use elements::{Module, Opcode, BlockType, FunctionType, ValueType, External, Type}; use elements::{Opcode, BlockType, FunctionType, ValueType};
use interpreter::Error; use interpreter::Error;
use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX}; use interpreter::runner::{DEFAULT_MEMORY_INDEX, DEFAULT_TABLE_INDEX};
use interpreter::imports::ModuleImports; use interpreter::module::{ModuleInstance, ModuleInstanceInterface, ItemIndex};
use interpreter::module::ItemIndex;
use interpreter::stack::StackWithLimit; use interpreter::stack::StackWithLimit;
use interpreter::variable::VariableType; use interpreter::variable::VariableType;
@ -13,10 +12,8 @@ const NATURAL_ALIGNMENT: u32 = 0xFFFFFFFF;
/// Function validation context. /// Function validation context.
pub struct FunctionValidationContext<'a> { pub struct FunctionValidationContext<'a> {
/// Wasm module. /// Wasm module instance (in process of instantiation).
module: &'a Module, module_instance: &'a ModuleInstance,
/// Module imports.
imports: &'a ModuleImports,
/// Current instruction position. /// Current instruction position.
position: usize, position: usize,
/// Local variables. /// Local variables.
@ -571,16 +568,14 @@ impl Validator {
impl<'a> FunctionValidationContext<'a> { impl<'a> FunctionValidationContext<'a> {
pub fn new( pub fn new(
module: &'a Module, module_instance: &'a ModuleInstance,
imports: &'a ModuleImports,
locals: &'a [ValueType], locals: &'a [ValueType],
value_stack_limit: usize, value_stack_limit: usize,
frame_stack_limit: usize, frame_stack_limit: usize,
function: &FunctionType, function: &FunctionType,
) -> Self { ) -> Self {
FunctionValidationContext { FunctionValidationContext {
module: module, module_instance: module_instance,
imports: imports,
position: 0, position: 0,
locals: locals, locals: locals,
value_stack: StackWithLimit::with_limit(value_stack_limit), value_stack: StackWithLimit::with_limit(value_stack_limit),
@ -692,100 +687,45 @@ impl<'a> FunctionValidationContext<'a> {
} }
pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<StackValueType, Error> { pub fn require_global(&self, idx: u32, mutability: Option<bool>) -> Result<StackValueType, Error> {
match self.imports.parse_global_index(ItemIndex::IndexSpace(idx)) { self.module_instance
ItemIndex::IndexSpace(_) => unreachable!("parse_global_index is intended to resolve this"), .global(ItemIndex::IndexSpace(idx), None)
ItemIndex::Internal(internal_idx) => self.module .and_then(|g| match mutability {
.global_section().ok_or(Error::Validation(format!("Trying to access internal global {} in module without global section", internal_idx))) Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be mutable", idx))),
.and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal global {} in module with {} globals", internal_idx, s.entries().len())))) Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected global {} to be immutable", idx))),
.and_then(|g| match mutability { _ => match g.variable_type() {
Some(true) if !g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be mutable", internal_idx))), VariableType::AnyFunc => Err(Error::Validation(format!("Expected global {} to have non-AnyFunc type", idx))),
Some(false) if g.global_type().is_mutable() => Err(Error::Validation(format!("Expected internal global {} to be immutable", internal_idx))), VariableType::I32 => Ok(StackValueType::Specific(ValueType::I32)),
_ => Ok(g), VariableType::I64 => Ok(StackValueType::Specific(ValueType::I64)),
}) VariableType::F32 => Ok(StackValueType::Specific(ValueType::F32)),
.map(|g| g.global_type().content_type().into()), VariableType::F64 => Ok(StackValueType::Specific(ValueType::F64)),
ItemIndex::External(external_idx) => self.module }
.import_section().ok_or(Error::Validation(format!("Trying to access external global {} in module without import section", external_idx))) })
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external global with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Global(ref g) => {
match mutability {
Some(true) if !g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be mutable", external_idx))),
Some(false) if g.is_mutable() => Err(Error::Validation(format!("Expected external global {} to be immutable", external_idx))),
_ => Ok(g.content_type().into()),
}
},
_ => Err(Error::Validation(format!("Import entry {} expected to import global", external_idx)))
}),
}
} }
pub fn require_memory(&self, idx: u32) -> Result<(), Error> { pub fn require_memory(&self, idx: u32) -> Result<(), Error> {
match self.imports.parse_memory_index(ItemIndex::IndexSpace(idx)) { self.module_instance
ItemIndex::IndexSpace(_) => unreachable!("parse_memory_index is intended to resolve this"), .memory(ItemIndex::IndexSpace(idx))
ItemIndex::Internal(internal_idx) => self.module .map(|_| ())
.memory_section().ok_or(Error::Validation(format!("Trying to access internal memory {} in module without memory section", internal_idx)))
.and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal memory {} in module with {} memory regions", internal_idx, s.entries().len()))))
.map(|_| ()),
ItemIndex::External(external_idx) => self.module
.import_section().ok_or(Error::Validation(format!("Trying to access external memory {} in module without import section", external_idx)))
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external memory with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Memory(_) => Ok(()),
_ => Err(Error::Validation(format!("Import entry {} expected to import memory", external_idx)))
}),
}
} }
pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> { pub fn require_table(&self, idx: u32, variable_type: VariableType) -> Result<(), Error> {
match self.imports.parse_table_index(ItemIndex::IndexSpace(idx)) { self.module_instance
ItemIndex::IndexSpace(_) => unreachable!("parse_table_index is intended to resolve this"), .table(ItemIndex::IndexSpace(idx))
ItemIndex::Internal(internal_idx) => self.module .and_then(|t| if t.variable_type() == variable_type {
.table_section().ok_or(Error::Validation(format!("Trying to access internal table {} in module without table section", internal_idx))) Ok(())
.and_then(|s| s.entries().get(internal_idx as usize).ok_or(Error::Validation(format!("Trying to access internal table {} in module with {} tables", internal_idx, s.entries().len())))) } else {
.and_then(|t| if variable_type == t.elem_type().into() { Err(Error::Validation(format!("Table {} has element type {:?} while {:?} expected", idx, t.variable_type(), variable_type)))
Ok(()) })
} else {
Err(Error::Validation(format!("Internal table {} has element type {:?} while {:?} expected", internal_idx, t.elem_type(), variable_type)))
}),
ItemIndex::External(external_idx) => self.module
.import_section().ok_or(Error::Validation(format!("Trying to access external table {} in module without import section", external_idx)))
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external table with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Table(ref t) => if variable_type == t.elem_type().into() {
Ok(())
} else {
Err(Error::Validation(format!("External table {} has element type {:?} while {:?} expected", external_idx, t.elem_type(), variable_type)))
},
_ => Err(Error::Validation(format!("Import entry {} expected to import table", external_idx)))
}),
}
} }
pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> { pub fn require_function(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
match self.imports.parse_function_index(ItemIndex::IndexSpace(idx)) { self.module_instance.function_type(ItemIndex::IndexSpace(idx))
ItemIndex::IndexSpace(_) => unreachable!("parse_function_index is intended to resolve this"), .map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
ItemIndex::Internal(internal_idx) => self.module
.function_section().ok_or(Error::Validation(format!("Trying to access internal function {} in module without function section", internal_idx)))
.and_then(|s| s.entries().get(internal_idx as usize).map(|f| f.type_ref()).ok_or(Error::Validation(format!("Trying to access internal function {} in module with {} functions", internal_idx, s.entries().len()))))
.and_then(|tidx| self.require_function_type(tidx)),
ItemIndex::External(external_idx) => self.module
.import_section().ok_or(Error::Validation(format!("Trying to access external function {} in module without import section", external_idx)))
.and_then(|s| s.entries().get(external_idx as usize).ok_or(Error::Validation(format!("Trying to access external function with index {} in module with {}-entries import section", external_idx, s.entries().len()))))
.and_then(|e| match e.external() {
&External::Function(tidx) => Ok(tidx),
_ => Err(Error::Validation(format!("Import entry {} expected to import function", external_idx)))
})
.and_then(|tidx| self.require_function_type(tidx)),
}
} }
pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> { pub fn require_function_type(&self, idx: u32) -> Result<(Vec<ValueType>, BlockType), Error> {
self.module self.module_instance.function_type_by_index(idx)
.type_section().ok_or(Error::Validation(format!("Trying to access internal function {} in module without type section", idx))) .map(|ft| (ft.params().to_vec(), ft.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult)))
.and_then(|ts| match ts.types().get(idx as usize) {
Some(&Type::Function(ref function_type)) => Ok((function_type.params().to_vec(), function_type.return_type().map(BlockType::Value).unwrap_or(BlockType::NoResult))),
_ => Err(Error::Validation(format!("Trying to access internal function {} with wrong type", idx))),
})
} }
pub fn function_labels(self) -> HashMap<usize, usize> { pub fn function_labels(self) -> HashMap<usize, usize> {