diff --git a/src/elements/mod.rs b/src/elements/mod.rs index dccc52b..b065155 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -19,7 +19,7 @@ pub use self::section::{ ImportSection, ExportSection, GlobalSection, TypeSection, ElementSection, TableSection, }; -pub use self::import_entry::{ImportEntry, MemoryType, TableType, GlobalType, External}; +pub use self::import_entry::{ImportEntry, ResizableLimits, MemoryType, TableType, GlobalType, External}; pub use self::export_entry::{ExportEntry, Internal}; pub use self::global_entry::GlobalEntry; pub use self::primitives::{ diff --git a/src/interpreter/imports.rs b/src/interpreter/imports.rs index 6588a6b..c5adedf 100644 --- a/src/interpreter/imports.rs +++ b/src/interpreter/imports.rs @@ -50,6 +50,16 @@ impl ModuleImports { } } + /// Number of imported tables. + pub fn tables_len(&self) -> usize { + self.tables.len() + } + + /// Number of imported memory regions. + pub fn memory_regions_len(&self) -> usize { + self.memory.len() + } + /// Parse function index. pub fn parse_function_index(&self, index: ItemIndex) -> ItemIndex { match index { diff --git a/src/interpreter/memory.rs b/src/interpreter/memory.rs index 35ee0a4..fa0b247 100644 --- a/src/interpreter/memory.rs +++ b/src/interpreter/memory.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use parking_lot::RwLock; use elements::MemoryType; use interpreter::Error; +use interpreter::module::check_limits; /// Linear memory page size. pub const LINEAR_MEMORY_PAGE_SIZE: u32 = 65536; @@ -18,6 +19,8 @@ pub struct MemoryInstance { impl MemoryInstance { /// Create new linear memory instance. pub fn new(memory_type: &MemoryType) -> Result, Error> { + check_limits(memory_type.limits())?; + let memory = MemoryInstance { buffer: RwLock::new(Vec::new()), // TODO: with_capacity maximum_size: memory_type.limits().maximum() diff --git a/src/interpreter/module.rs b/src/interpreter/module.rs index 7df2be6..5273dd3 100644 --- a/src/interpreter/module.rs +++ b/src/interpreter/module.rs @@ -1,7 +1,7 @@ -use std::collections::HashMap; +use std::collections::{BTreeSet, HashMap}; use std::iter::repeat; use std::sync::{Arc, Weak}; -use elements::{Module, InitExpr, Opcode, Type, FunctionType, FuncBody, Internal, BlockType}; +use elements::{Module, InitExpr, Opcode, Type, FunctionType, FuncBody, Internal, External, BlockType, ResizableLimits}; use interpreter::Error; use interpreter::imports::ModuleImports; use interpreter::memory::MemoryInstance; @@ -122,7 +122,7 @@ impl ModuleInstance { } /// Instantiate given module within program context. - pub fn new_with_validation_flag(program: Weak, module: Module, requires_validation: bool) -> Result { + pub fn new_with_validation_flag(program: Weak, module: Module, is_user_module: bool) -> Result { // load entries from import section let imports = ModuleImports::new(program, module.import_section()); @@ -164,32 +164,92 @@ impl ModuleInstance { tables: tables, globals: globals, }; - module.complete_initialization(requires_validation)?; + module.complete_initialization(is_user_module)?; Ok(module) } /// Complete module initialization. - fn complete_initialization(&mut self, requires_validation: bool) -> Result<(), Error> { - if requires_validation { - // TODO: missing module validation step + fn complete_initialization(&mut self, is_user_module: bool) -> Result<(), Error> { + // validate start section + if let Some(start_function) = self.module.start_section() { + self.require_function(ItemIndex::IndexSpace(start_function))?; + } - // validate every function body - match (self.module.function_section(), self.module.code_section(), self.module.type_section()) { - (Some(function_section), Some(code_section), Some(type_section)) => { - for (index, function) in function_section.entries().iter().enumerate() { - let function_type = match type_section.types().get(function.type_ref() as usize) { - Some(&Type::Function(ref function_type)) => function_type.clone(), - _ => return Err(Error::Validation(format!("Missing type 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(); - 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); - Validator::validate_block(&mut context, block_type, function_body.code().elements(), Opcode::End)?; + // validate export section + if is_user_module { // TODO: env module exports STACKTOP global, which is mutable => check is failed + if let Some(export_section) = self.module.export_section() { + // duplicate name check is not on specification, but it seems logical + let mut names = BTreeSet::new(); + for export in export_section.entries() { + if !names.insert(export.field()) { + return Err(Error::Validation(format!("duplicate export with name {}", export.field()))); } - }, - _ => (), + + // this allows reexporting + match export.internal() { + &Internal::Function(function_index) => + self.require_function(ItemIndex::IndexSpace(function_index))?, + &Internal::Global(global_index) => + self.global(ItemIndex::IndexSpace(global_index)) + .and_then(|g| if g.is_mutable() { + Err(Error::Validation(format!("trying to export mutable global {}", export.field()))) + } else { + Ok(()) + })?, + &Internal::Memory(memory_index) => + self.memory(ItemIndex::IndexSpace(memory_index)).map(|_| ())?, + &Internal::Table(table_index) => + self.table(ItemIndex::IndexSpace(table_index)).map(|_| ())?, + } + } + } + } + + // validate import section + if let Some(import_section) = self.module.import_section() { + // external module + its export existance is checked on runtime + for import in import_section.entries() { + match import.external() { + &External::Function(ref function_index) => self.require_function_type(*function_index).map(|_| ())?, + &External::Global(ref global_type) => if global_type.is_mutable() { + return Err(Error::Validation(format!("trying to import mutable global {}", import.field()))) + }, + &External::Memory(ref memory_type) => check_limits(memory_type.limits())?, + &External::Table(ref table_type) => check_limits(table_type.limits())?, + } + } + } + + // there must be no greater than 1 table in tables index space + if self.imports.tables_len() + self.tables.len() > 1 { + return Err(Error::Validation(format!("too many tables in index space: {}", self.imports.tables_len() + self.tables.len()))); + } + + // there must be no greater than 1 memory region in memory regions index space + if self.imports.memory_regions_len() + self.memory.len() > 1 { + return Err(Error::Validation(format!("too many memory regions in index space: {}", self.imports.memory_regions_len() + self.memory.len()))); + } + + // for every function section entry there must be corresponding entry in code section and type && vice versa + let function_section_len = self.module.function_section().map(|s| s.entries().len()).unwrap_or(0); + let code_section_len = self.module.code_section().map(|s| s.bodies().len()).unwrap_or(0); + if function_section_len != code_section_len { + return Err(Error::Validation(format!("length of function section is {}, while len of code section is {}", function_section_len, code_section_len))); + } + + // validate every function body in user modules + if is_user_module && function_section_len != 0 { + let function_section = self.module.function_section().expect("function_section_len != 0; qed"); + let code_section = self.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_type = self.require_function_type(function.type_ref())?; + 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(); + 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); + Validator::validate_block(&mut context, block_type, function_body.code().elements(), Opcode::End)?; } } @@ -208,6 +268,10 @@ impl ModuleInstance { if let Some(element_section) = self.module.elements_section() { for (element_segment_index, element_segment) in element_section.entries().iter().enumerate() { let offset: u32 = get_initializer(element_segment.offset(), &self.module, &self.imports)?.try_into()?; + for function_index in element_segment.members() { + self.require_function(ItemIndex::IndexSpace(*function_index))?; + } + self.table(ItemIndex::IndexSpace(element_segment.index())) .map_err(|e| Error::Initialization(format!("ElementSegment {} initializes non-existant Table {}: {:?}", element_segment_index, element_segment.index(), e))) .and_then(|m| m.set_raw(offset, element_segment.members())) @@ -217,6 +281,31 @@ impl ModuleInstance { Ok(()) } + + fn require_function(&self, index: ItemIndex) -> Result<(), Error> { + match self.imports.parse_function_index(index) { + ItemIndex::IndexSpace(_) => unreachable!("parse_function_index resolves IndexSpace option"), + ItemIndex::Internal(index) => self.module.function_section() + .ok_or(Error::Function(format!("initializing table element to value {}, without corresponding internal function", index))) + .and_then(|s| s.entries().get(index as usize) + .ok_or(Error::Function(format!("initializing table element to value {}, without corresponding internal function", index)))) + .map(|_| ()), + ItemIndex::External(index) => self.module.import_section() + .ok_or(Error::Function(format!("initializing table element to value {}, without corresponding external function", index))) + .and_then(|s| s.entries().get(index as usize) + .ok_or(Error::Function(format!("initializing table element to value {}, without corresponding external function", index)))) + .map(|_| ()), + } + } + + fn require_function_type(&self, type_index: u32) -> Result<&FunctionType, Error> { + self.module.type_section() + .ok_or(Error::Validation(format!("type reference {} exists in module without type section", type_index))) + .and_then(|s| match s.types().get(type_index as usize) { + Some(&Type::Function(ref function_type)) => Ok(function_type), + _ => Err(Error::Validation(format!("missing function type with index {}", type_index))), + }) + } } impl ModuleInstanceInterface for ModuleInstance { @@ -415,6 +504,16 @@ impl<'a> CallerContext<'a> { } } +pub fn check_limits(limits: &ResizableLimits) -> Result<(), Error> { + if let Some(maximum) = limits.maximum() { + if maximum < limits.initial() { + return Err(Error::Validation(format!("maximum limit {} is lesser than minimum {}", maximum, limits.initial()))); + } + } + + Ok(()) +} + fn prepare_function_locals(function_type: &FunctionType, function_body: &FuncBody, outer: &mut CallerContext) -> Result, Error> { // locals = function arguments + defined locals function_type.params().iter().rev() diff --git a/src/interpreter/table.rs b/src/interpreter/table.rs index 6a86e4e..52d6d23 100644 --- a/src/interpreter/table.rs +++ b/src/interpreter/table.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use parking_lot::RwLock; use elements::TableType; use interpreter::Error; +use interpreter::module::check_limits; use interpreter::variable::{VariableInstance, VariableType}; use interpreter::value::RuntimeValue; @@ -17,6 +18,8 @@ pub struct TableInstance { impl TableInstance { /// New instance of the table pub fn new(variable_type: VariableType, table_type: &TableType) -> Result, Error> { + check_limits(table_type.limits())?; + Ok(Arc::new(TableInstance { variable_type: variable_type, buffer: RwLock::new( diff --git a/src/interpreter/variable.rs b/src/interpreter/variable.rs index 79a2ff2..74ebde3 100644 --- a/src/interpreter/variable.rs +++ b/src/interpreter/variable.rs @@ -32,6 +32,7 @@ pub struct VariableInstance { impl VariableInstance { /// New variable instance pub fn new(is_mutable: bool, variable_type: VariableType, value: RuntimeValue) -> Result { + // TODO: there is nothing about null value in specification + there is nothing about initializing missing table elements? => runtime check for nulls if !value.is_null() && value.variable_type() != Some(variable_type) { return Err(Error::Variable(format!("trying to initialize variable of type {:?} with value of type {:?}", variable_type, value.variable_type()))); } @@ -48,6 +49,11 @@ impl VariableInstance { Self::new(global_type.is_mutable(), global_type.content_type().into(), value) } + /// Is mutable + pub fn is_mutable(&self) -> bool { + self.is_mutable + } + /// Get the value of the variable instance pub fn get(&self) -> RuntimeValue { self.value.read().clone()