From f75a4963741e5cab3f31c60f0162232d6bed8d25 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 24 Jun 2020 11:46:08 -0700 Subject: [PATCH 1/2] wasmpaser: Validate nested modules This commit implements recursive validation of nested modules in wasmparser. Previously nested modules were simply skipped but now they're recursed into and actually checked. This commit also comes with a more complete implementation of handling `alias` directives. Internally this required quite a bit of refactoring. The validator now retains a stack of modules which are being validated and remembers parent-relationships between them. Indices into this stack are then used for recording type definitions. The type of a function/instance/module/etc can be defined the current module or any previous module on the stack. New helper functiosn were added to help resolve this new `Def` type to ensure it's handled correctly. --- crates/wasmparser/src/module_resources.rs | 6 +- crates/wasmparser/src/operators_validator.rs | 5 +- crates/wasmparser/src/parser.rs | 17 + crates/wasmparser/src/validator.rs | 899 +++++++++++------- tests/local/module-linking/alias.wast | 22 +- tests/local/module-linking/infer-types.wast | 2 +- tests/local/module-linking/instantiate.wast | 6 +- .../local/module-linking/nested-modules.wast | 10 + tests/local/module-linking/types.wast | 11 + tests/local/module-linking/virtualize.wast | 4 + 10 files changed, 627 insertions(+), 355 deletions(-) diff --git a/crates/wasmparser/src/module_resources.rs b/crates/wasmparser/src/module_resources.rs index 5f83835a73..4ed1c4d586 100644 --- a/crates/wasmparser/src/module_resources.rs +++ b/crates/wasmparser/src/module_resources.rs @@ -302,7 +302,7 @@ pub trait WasmModuleResources { /// Returns the global variable at given index. fn global_at(&self, at: u32) -> Option<&Self::GlobalType>; /// Returns the function signature ID at given index. - fn func_type_id_at(&self, at: u32) -> Option; + fn func_type_at(&self, at: u32) -> Option<&::FuncType>; /// Returns the element type at the given index. fn element_type_at(&self, at: u32) -> Option; @@ -336,8 +336,8 @@ where fn global_at(&self, at: u32) -> Option<&Self::GlobalType> { T::global_at(self, at) } - fn func_type_id_at(&self, at: u32) -> Option { - T::func_type_id_at(self, at) + fn func_type_at(&self, at: u32) -> Option<&::FuncType> { + T::func_type_at(self, at) } fn element_type_at(&self, at: u32) -> Option { T::element_type_at(self, at) diff --git a/crates/wasmparser/src/operators_validator.rs b/crates/wasmparser/src/operators_validator.rs index ad16a6926a..b3a4c32064 100644 --- a/crates/wasmparser/src/operators_validator.rs +++ b/crates/wasmparser/src/operators_validator.rs @@ -523,7 +523,7 @@ impl OperatorValidator { function_index: u32, resources: impl WasmModuleResources, ) -> OperatorValidatorResult<()> { - let type_index = match resources.func_type_id_at(function_index) { + let ty = match resources.func_type_at(function_index) { Some(i) => i, None => { bail_op_err!( @@ -532,7 +532,6 @@ impl OperatorValidator { ); } }; - let ty = func_type_at(&resources, type_index)?; self.check_operands(wasm_func_type_inputs(ty).map(WasmType::to_parser_type))?; self.func_state.change_frame_with_types( ty.len_inputs(), @@ -1557,7 +1556,7 @@ impl OperatorValidator { } Operator::RefFunc { function_index } => { self.check_reference_types_enabled()?; - if resources.func_type_id_at(function_index).is_none() { + if resources.func_type_at(function_index).is_none() { return Err(OperatorValidatorError::new( "unknown function: function index out of bounds", )); diff --git a/crates/wasmparser/src/parser.rs b/crates/wasmparser/src/parser.rs index 2263bf22ba..03b3f5ac85 100644 --- a/crates/wasmparser/src/parser.rs +++ b/crates/wasmparser/src/parser.rs @@ -1294,3 +1294,20 @@ impl<'a> WasmDecoder<'a> for Parser<'a> { &self.state } } + +impl<'a> From> for Parser<'a> { + fn from(reader: ModuleReader<'a>) -> Parser<'a> { + let mut parser = Parser::default(); + parser.state = ParserState::BeginWasm { + version: reader.get_version(), + }; + parser.module_reader = Some(reader); + return parser; + } +} + +impl<'a> Default for Parser<'a> { + fn default() -> Parser<'a> { + Parser::new(&[]) + } +} diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 11160eb92d..d273c94179 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -14,6 +14,7 @@ */ use std::collections::{HashMap, HashSet}; +use std::mem; use std::result; use std::str; @@ -33,7 +34,7 @@ use crate::operators_validator::{ }; use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder}; use crate::{AliasedInstance, WasmModuleResources}; -use crate::{ElemSectionEntryTable, ElementItem, WasmTypeDef}; +use crate::{ElemSectionEntryTable, ElementItem}; use crate::readers::FunctionBody; @@ -46,7 +47,7 @@ struct InitExpressionState { validated: bool, } -#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)] enum SectionOrderState { Initial, Type, @@ -97,6 +98,12 @@ impl SectionOrderState { } } +impl Default for SectionOrderState { + fn default() -> SectionOrderState { + SectionOrderState::Initial + } +} + #[derive(Copy, Clone)] pub struct ValidatingParserConfig { pub operator_config: OperatorValidatorConfig, @@ -106,81 +113,76 @@ const DEFAULT_VALIDATING_PARSER_CONFIG: ValidatingParserConfig = ValidatingParse operator_config: DEFAULT_OPERATOR_VALIDATOR_CONFIG, }; +#[derive(Default)] struct ValidatingParserResources<'a> { - types: Vec>, - tables: Vec, + types: Vec>, + tables: Vec>, memories: Vec, - globals: Vec, + globals: Vec>, element_types: Vec, data_count: Option, - func_type_indices: Vec, - module_type_indices: Vec, - instance_type_indices: Vec, + func_type_indices: Vec>, + module_type_indices: Vec>, + instance_type_indices: Vec>, function_references: HashSet, } -enum InstanceDef { - Imported { type_idx: u32 }, - Instantiated { module_idx: u32 }, +#[derive(Copy, Clone)] +struct Def { + module: usize, + item: T, } -impl<'a> WasmModuleResources for ValidatingParserResources<'a> { - type TypeDef = crate::TypeDef<'a>; - type TableType = crate::TableType; - type MemoryType = crate::MemoryType; - type GlobalType = crate::GlobalType; - - fn type_at(&self, at: u32) -> Option<&Self::TypeDef> { - self.types.get(at as usize) - } - - fn table_at(&self, at: u32) -> Option<&Self::TableType> { - self.tables.get(at as usize) - } - - fn memory_at(&self, at: u32) -> Option<&Self::MemoryType> { - self.memories.get(at as usize) - } - - fn global_at(&self, at: u32) -> Option<&Self::GlobalType> { - self.globals.get(at as usize) - } - - fn func_type_id_at(&self, at: u32) -> Option { - self.func_type_indices.get(at as usize).copied() - } - - fn element_type_at(&self, at: u32) -> Option { - self.element_types.get(at as usize).cloned() - } +enum ValidatedType<'a> { + Def(TypeDef<'a>), + Alias(Def), +} - fn element_count(&self) -> u32 { - self.element_types.len() as u32 +impl Def { + fn as_ref(&self) -> Def<&T> { + Def { + module: self.module, + item: &self.item, + } } - fn data_count(&self) -> u32 { - self.data_count.unwrap_or(0) + fn map(self, map: impl FnOnce(T) -> U) -> Def { + Def { + module: self.module, + item: map(self.item), + } } +} - fn is_function_referenced(&self, idx: u32) -> bool { - self.function_references.contains(&idx) - } +enum InstanceDef { + Imported { type_idx: u32 }, + Instantiated { module_idx: u32 }, } pub struct ValidatingParser<'a> { parser: Parser<'a>, validation_error: Option>, read_position: Option, + init_expression_state: Option, + current_operator_validator: Option, + /// Once we see a `BeginInstantiate` this tracks the type index of the + /// module type as well as which import index we're currently matching + /// against. + module_instantiation: Option<(Def, usize)>, + config: ValidatingParserConfig, + modules: Vec>, + total_nested_modules: usize, +} + +#[derive(Default)] +struct Module<'a> { + parser: Parser<'a>, section_order_state: SectionOrderState, resources: ValidatingParserResources<'a>, current_func_index: u32, func_nonlocal_count: u32, - init_expression_state: Option, data_found: u32, exported_names: HashSet, - current_operator_validator: Option, - module_instantiation: Option<(u32, usize)>, - config: ValidatingParserConfig, } impl<'a> ValidatingParser<'a> { @@ -189,34 +191,15 @@ impl<'a> ValidatingParser<'a> { parser: Parser::new(bytes), validation_error: None, read_position: None, - section_order_state: SectionOrderState::Initial, - resources: ValidatingParserResources { - types: Vec::new(), - tables: Vec::new(), - memories: Vec::new(), - globals: Vec::new(), - element_types: Vec::new(), - data_count: None, - func_type_indices: Vec::new(), - instance_type_indices: Vec::new(), - module_type_indices: Vec::new(), - function_references: HashSet::new(), - }, - current_func_index: 0, - func_nonlocal_count: 0, current_operator_validator: None, init_expression_state: None, - data_found: 0, - exported_names: HashSet::new(), module_instantiation: None, config: config.unwrap_or(DEFAULT_VALIDATING_PARSER_CONFIG), + modules: vec![Module::default()], + total_nested_modules: 0, } } - pub fn get_resources<'b>(&'b self) -> impl WasmModuleResources + 'b { - &self.resources - } - fn set_validation_error(&mut self, message: impl Into) { self.validation_error = Some(ParserState::Error(BinaryReaderError::new( message, @@ -335,19 +318,27 @@ impl<'a> ValidatingParser<'a> { self.check_value_type(global_type.content_type) } + fn cur_module(&self) -> &Module<'a> { + self.modules.last().unwrap() + } + + fn cur_module_mut(&mut self) -> &mut Module<'a> { + self.modules.last_mut().unwrap() + } + fn check_import_entry(&self, import_type: &ImportSectionEntryType) -> ValidatorResult<'a, ()> { match *import_type { ImportSectionEntryType::Function(type_index) => { - if self.resources.func_type_indices.len() >= MAX_WASM_FUNCTIONS { + if self.cur_module().resources.func_type_indices.len() >= MAX_WASM_FUNCTIONS { return self.create_error("functions count out of bounds"); } - self.func_type_at(type_index)?; + self.func_type_at(self.def(type_index))?; Ok(()) } ImportSectionEntryType::Table(ref table_type) => { if !self.config.operator_config.enable_reference_types && !self.config.operator_config.enable_module_linking - && self.resources.tables.len() >= MAX_WASM_TABLES + && self.cur_module().resources.tables.len() >= MAX_WASM_TABLES { return self.create_error("multiple tables: tables count must be at most 1"); } @@ -355,30 +346,30 @@ impl<'a> ValidatingParser<'a> { } ImportSectionEntryType::Memory(ref memory_type) => { if !self.config.operator_config.enable_module_linking - && self.resources.memories.len() >= MAX_WASM_MEMORIES + && self.cur_module().resources.memories.len() >= MAX_WASM_MEMORIES { return self.create_error("multiple memories: memory count must be at most 1"); } self.check_memory_type(memory_type) } ImportSectionEntryType::Global(global_type) => { - if self.resources.globals.len() >= MAX_WASM_GLOBALS { + if self.cur_module().resources.globals.len() >= MAX_WASM_GLOBALS { return self.create_error("globals count out of bounds"); } self.check_global_type(global_type) } ImportSectionEntryType::Module(type_index) => { - if self.resources.module_type_indices.len() >= MAX_WASM_MODULES { + if self.cur_module().resources.module_type_indices.len() >= MAX_WASM_MODULES { return self.create_error("modules count out of bounds"); } - self.module_type_at(type_index)?; + self.module_type_at(self.def(type_index))?; Ok(()) } ImportSectionEntryType::Instance(type_index) => { - if self.resources.instance_type_indices.len() >= MAX_WASM_INSTANCES { + if self.cur_module().resources.instance_type_indices.len() >= MAX_WASM_INSTANCES { return self.create_error("instance count out of bounds"); } - self.instance_type_at(type_index)?; + self.instance_type_at(self.def(type_index))?; Ok(()) } } @@ -391,6 +382,7 @@ impl<'a> ValidatingParser<'a> { "constant expression required: type mismatch: only one init_expr operator is expected", ); } + let expected_ty = state.ty; let ty = match operator { Operator::I32Const { .. } => Type::I32, Operator::I64Const { .. } => Type::I64, @@ -413,7 +405,7 @@ impl<'a> ValidatingParser<'a> { return self .create_error("unknown global: init_expr global index out of bounds"); } - self.resources.globals[global_index as usize].content_type + self.get_global(self.def(global_index))?.item.content_type } Operator::RefFunc { function_index } => { if function_index as usize >= state.function_count { @@ -422,7 +414,10 @@ impl<'a> ValidatingParser<'a> { function_index )); } - self.resources.function_references.insert(function_index); + self.cur_module_mut() + .resources + .function_references + .insert(function_index); Type::FuncRef } _ => { @@ -430,7 +425,7 @@ impl<'a> ValidatingParser<'a> { .create_error("constant expression required: invalid init_expr operator") } }; - if ty != state.ty { + if ty != expected_ty { return self.create_error("type mismatch: invalid init_expr type"); } Ok(()) @@ -442,7 +437,7 @@ impl<'a> ValidatingParser<'a> { kind: ExternalKind, index: u32, ) -> ValidatorResult<'a, ()> { - if self.exported_names.contains(field) { + if self.cur_module().exported_names.contains(field) { return self.create_error("duplicate export name"); } if let ExternalKind::Type = kind { @@ -458,13 +453,14 @@ impl<'a> ValidatingParser<'a> { kind: ExternalKind, index: u32, ) -> ValidatorResult<'a, ()> { + let module = self.cur_module_mut(); let (ty, total) = match kind { - ExternalKind::Function => ("function", self.resources.func_type_indices.len()), - ExternalKind::Table => ("table", self.resources.tables.len()), - ExternalKind::Memory => ("memory", self.resources.memories.len()), - ExternalKind::Global => ("global", self.resources.globals.len()), - ExternalKind::Module => ("module", self.resources.module_type_indices.len()), - ExternalKind::Instance => ("instance", self.resources.instance_type_indices.len()), + ExternalKind::Function => ("function", module.resources.func_type_indices.len()), + ExternalKind::Table => ("table", module.resources.tables.len()), + ExternalKind::Memory => ("memory", module.resources.memories.len()), + ExternalKind::Global => ("global", module.resources.globals.len()), + ExternalKind::Module => ("module", module.resources.module_type_indices.len()), + ExternalKind::Instance => ("instance", module.resources.instance_type_indices.len()), ExternalKind::Type => return self.create_error("cannot export types"), }; if index as usize >= total { @@ -474,55 +470,133 @@ impl<'a> ValidatingParser<'a> { )); } if let ExternalKind::Function = kind { - self.resources.function_references.insert(index); + module.resources.function_references.insert(index); } Ok(()) } - fn type_at<'me>(&'me self, type_index: u32) -> ValidatorResult<'a, &'me TypeDef<'a>> { - match self.resources.types.get(type_index as usize) { + fn def(&self, item: T) -> Def { + Def { + module: self.modules.len() - 1, + item, + } + } + + fn current_func_index(&self) -> Def { + let module = &self.cur_module(); + self.def(module.current_func_index + module.func_nonlocal_count) + } + + fn get<'me, T>( + &'me self, + idx: Def, + desc: &str, + get_list: impl FnOnce(&'me ValidatingParserResources<'a>) -> &'me [T], + ) -> ValidatorResult<'a, &'me T> { + match get_list(&self.modules[idx.module].resources).get(idx.item as usize) { Some(ty) => Ok(ty), - None => self.create_error("unknown type: type index out of bounds"), + None => self.create_error(&format!("unknown {0}: {0} index out of bounds", desc)), + } + } + + fn get_type<'me>(&'me self, mut idx: Def) -> ValidatorResult<'a, Def<&'me TypeDef<'a>>> { + loop { + let def = self.get(idx, "type", |v| &v.types)?; + match def { + ValidatedType::Def(item) => { + break Ok(Def { + module: idx.module, + item, + }) + } + ValidatedType::Alias(other) => idx = *other, + } } } - fn func_type_at<'me>(&'me self, type_index: u32) -> ValidatorResult<'a, &'me FuncType> { - match self.type_at(type_index)? { - TypeDef::Func(f) => Ok(f), + fn get_table<'me>(&'me self, idx: Def) -> ValidatorResult<'a, &'me Def> { + self.get(idx, "table", |v| &v.tables) + } + + fn get_memory<'me>(&'me self, idx: Def) -> ValidatorResult<'a, &'me MemoryType> { + self.get(idx, "memory", |v| &v.memories) + } + + fn get_global<'me>(&'me self, idx: Def) -> ValidatorResult<'a, &'me Def> { + self.get(idx, "global", |v| &v.globals) + } + + fn get_func_type_index<'me>(&'me self, idx: Def) -> ValidatorResult<'a, Def> { + Ok(*self.get(idx, "func", |v| &v.func_type_indices)?) + } + + fn get_module_type_index<'me>(&'me self, idx: Def) -> ValidatorResult<'a, Def> { + Ok(*self.get(idx, "module", |v| &v.module_type_indices)?) + } + + fn get_instance_def<'me>( + &'me self, + idx: Def, + ) -> ValidatorResult<'a, &'me Def> { + self.get(idx, "module", |v| &v.instance_type_indices) + } + + fn func_type_at<'me>( + &'me self, + type_index: Def, + ) -> ValidatorResult<'a, Def<&'me FuncType>> { + let def = self.get_type(type_index)?; + match &def.item { + TypeDef::Func(item) => Ok(Def { + module: def.module, + item, + }), _ => self.create_error("type index is not a function"), } } - fn module_type_at<'me>(&'me self, type_index: u32) -> ValidatorResult<'a, &'me ModuleType<'a>> { + fn module_type_at<'me>( + &'me self, + type_index: Def, + ) -> ValidatorResult<'a, Def<&'me ModuleType<'a>>> { if !self.config.operator_config.enable_module_linking { return self.create_error("module linking proposal not enabled"); } - match self.type_at(type_index)? { - TypeDef::Module(m) => Ok(m), + let ty = self.get_type(type_index)?; + match &ty.item { + TypeDef::Module(item) => Ok(Def { + module: ty.module, + item, + }), _ => self.create_error("type index is not a module"), } } fn instance_type_at<'me>( &'me self, - type_index: u32, - ) -> ValidatorResult<'a, &'me InstanceType<'a>> { + type_index: Def, + ) -> ValidatorResult<'a, Def<&'me InstanceType<'a>>> { if !self.config.operator_config.enable_module_linking { return self.create_error("module linking proposal not enabled"); } - match self.type_at(type_index)? { - TypeDef::Instance(i) => Ok(i), + let def = self.get_type(type_index)?; + match &def.item { + TypeDef::Instance(item) => Ok(Def { + module: def.module, + item, + }), _ => self.create_error("type index is not an instance"), } } fn check_start(&self, func_index: u32) -> ValidatorResult<'a, ()> { - if func_index as usize >= self.resources.func_type_indices.len() { - return self.create_error("unknown function: start function index out of bounds"); - } - let type_index = self.resources.func_type_indices[func_index as usize]; - let ty = self.func_type_at(type_index)?; - if !ty.params.is_empty() || !ty.returns.is_empty() { + let ty = match self.get_func_type_index(self.def(func_index)) { + Ok(ty) => self.func_type_at(ty)?, + Err(_) => { + return self.create_error("unknown function: start function index out of bounds") + } + }; + if !ty.item.params.is_empty() || !ty.item.returns.is_empty() { return self.create_error("invlid start function type"); } Ok(()) @@ -534,9 +608,9 @@ impl<'a> ValidatingParser<'a> { let state = SectionOrderState::from_section_code(code, &self.config); let state = match state { Some(state) => state, - None => return Ok(self.section_order_state), + None => return Ok(self.cur_module().section_order_state), }; - Ok(match self.section_order_state { + Ok(match self.cur_module().section_order_state { // Did we just start? In that case move to our newly-found state. Initial => state, @@ -570,7 +644,7 @@ impl<'a> ValidatingParser<'a> { if check.is_err() { self.validation_error = check.err(); } else { - self.section_order_state = check.ok().unwrap(); + self.cur_module_mut().section_order_state = check.ok().unwrap(); } } ParserState::TypeSectionEntry(ref def) => { @@ -581,10 +655,11 @@ impl<'a> ValidatingParser<'a> { }; if check.is_err() { self.validation_error = check.err(); - } else if self.resources.types.len() > MAX_WASM_TYPES { + } else if self.cur_module().resources.types.len() > MAX_WASM_TYPES { self.set_validation_error("types count is out of bounds"); } else { - self.resources.types.push(def.clone()); + let def = ValidatedType::Def(def.clone()); + self.cur_module_mut().resources.types.push(def); } } ParserState::ImportSectionEntry { ref ty, .. } => { @@ -594,77 +669,88 @@ impl<'a> ValidatingParser<'a> { } else { match *ty { ImportSectionEntryType::Function(type_index) => { - self.func_nonlocal_count += 1; - self.resources.func_type_indices.push(type_index); + let def = self.def(type_index); + self.cur_module_mut().resources.func_type_indices.push(def); + self.cur_module_mut().func_nonlocal_count += 1; } ImportSectionEntryType::Table(ref table_type) => { - self.resources.tables.push(table_type.clone()); + let def = self.def(table_type.clone()); + self.cur_module_mut().resources.tables.push(def); } ImportSectionEntryType::Memory(ref memory_type) => { - self.resources.memories.push(memory_type.clone()); + let ty = memory_type.clone(); + self.cur_module_mut().resources.memories.push(ty); } ImportSectionEntryType::Global(ref global_type) => { - self.resources.globals.push(global_type.clone()); + let def = self.def(global_type.clone()); + self.cur_module_mut().resources.globals.push(def); } + ImportSectionEntryType::Instance(type_index) => { - self.resources + let def = self.def(InstanceDef::Imported { + type_idx: type_index, + }); + self.cur_module_mut() + .resources .instance_type_indices - .push(InstanceDef::Imported { - type_idx: type_index, - }); + .push(def); } ImportSectionEntryType::Module(type_index) => { - self.resources.module_type_indices.push(type_index); + let def = self.def(type_index); + self.cur_module_mut() + .resources + .module_type_indices + .push(def); } } } } ParserState::FunctionSectionEntry(type_index) => { - if type_index as usize >= self.resources.types.len() { - self.set_validation_error("unknown type: func type index out of bounds"); - } else if self.resources.func_type_indices.len() >= MAX_WASM_FUNCTIONS { + let type_index = self.def(type_index); + if self.cur_module().resources.func_type_indices.len() >= MAX_WASM_FUNCTIONS { self.set_validation_error("functions count out of bounds"); } else if let Err(err) = self.func_type_at(type_index) { self.validation_error = Some(err); } else { - self.resources.func_type_indices.push(type_index); + self.cur_module_mut() + .resources + .func_type_indices + .push(type_index); } } ParserState::TableSectionEntry(ref table_type) => { if !self.config.operator_config.enable_reference_types && !self.config.operator_config.enable_module_linking - && self.resources.tables.len() >= MAX_WASM_TABLES + && self.cur_module().resources.tables.len() >= MAX_WASM_TABLES { self.set_validation_error("multiple tables: tables count must be at most 1"); } else { self.validation_error = self.check_table_type(table_type).err(); - self.resources.tables.push(table_type.clone()); + let def = self.def(table_type.clone()); + self.cur_module_mut().resources.tables.push(def); } } ParserState::MemorySectionEntry(ref memory_type) => { if !self.config.operator_config.enable_module_linking - && self.resources.memories.len() >= MAX_WASM_MEMORIES + && self.cur_module().resources.memories.len() >= MAX_WASM_MEMORIES { self.set_validation_error( "multiple memories: memories count must be at most 1", ); } else { self.validation_error = self.check_memory_type(memory_type).err(); - self.resources.memories.push(memory_type.clone()); + let ty = memory_type.clone(); + self.cur_module_mut().resources.memories.push(ty); } } ParserState::BeginGlobalSectionEntry(global_type) => { - if self.resources.globals.len() >= MAX_WASM_GLOBALS { + if self.cur_module().resources.globals.len() >= MAX_WASM_GLOBALS { self.set_validation_error("globals count out of bounds"); } else { self.validation_error = self.check_global_type(global_type).err(); - self.init_expression_state = Some(InitExpressionState { - ty: global_type.content_type, - global_count: self.resources.globals.len(), - function_count: self.resources.func_type_indices.len(), - validated: false, - }); - self.resources.globals.push(global_type); + self.set_init_expression_state(global_type.content_type); + let def = self.def(global_type); + self.cur_module_mut().resources.globals.push(def); } } ParserState::BeginInitExpressionBody => { @@ -683,37 +769,34 @@ impl<'a> ValidatingParser<'a> { } ParserState::ExportSectionEntry { field, kind, index } => { self.validation_error = self.check_export_entry(field, kind, index).err(); - self.exported_names.insert(String::from(field)); + self.cur_module_mut() + .exported_names + .insert(String::from(field)); } ParserState::StartSectionEntry(func_index) => { self.validation_error = self.check_start(func_index).err(); } ParserState::DataCountSectionEntry(count) => { - self.resources.data_count = Some(count); + self.cur_module_mut().resources.data_count = Some(count); } ParserState::BeginElementSectionEntry { table, ty } => { - self.resources.element_types.push(ty); + self.cur_module_mut().resources.element_types.push(ty); match table { ElemSectionEntryTable::Active(table_index) => { - let table = match self.resources.tables.get(table_index as usize) { - Some(t) => t, - None => { + let table = match self.get_table(self.def(table_index)) { + Ok(table) => table, + Err(_) => { self.set_validation_error( "unknown table: element section table index out of bounds", ); return; } }; - if ty != table.element_type { + if ty != table.item.element_type { self.set_validation_error("element_type != table type"); return; } - self.init_expression_state = Some(InitExpressionState { - ty: Type::I32, - global_count: self.resources.globals.len(), - function_count: self.resources.func_type_indices.len(), - validated: false, - }); + self.set_init_expression_state(Type::I32); } ElemSectionEntryTable::Passive | ElemSectionEntryTable::Declared => { if !self.config.operator_config.enable_bulk_memory { @@ -738,31 +821,35 @@ impl<'a> ValidatingParser<'a> { } } ParserState::ElementSectionEntryBody(ref indices) => { + let mut references = Vec::with_capacity(indices.len()); for item in &**indices { - if let ElementItem::Func(func_index) = item { - if *func_index as usize >= self.resources.func_type_indices.len() { + if let ElementItem::Func(func_index) = *item { + if self.get_func_type_index(self.def(func_index)).is_err() { self.set_validation_error( "unknown function: element func index out of bounds", ); break; } - self.resources.function_references.insert(*func_index); + references.push(func_index); } } + self.cur_module_mut() + .resources + .function_references + .extend(references); } ParserState::BeginFunctionBody { .. } => { - let index = (self.current_func_index + self.func_nonlocal_count) as usize; - if index as usize >= self.resources.func_type_indices.len() { + let index = self.current_func_index(); + if self.get_func_type_index(index).is_err() { self.set_validation_error("func type is not defined"); } } ParserState::FunctionBodyLocals { ref locals } => { - let index = (self.current_func_index + self.func_nonlocal_count) as usize; - let func_type = self - .func_type_at(self.resources.func_type_indices[index]) - .unwrap(); + let index = self.current_func_index(); + let func_type = self.get_func_type_index(index).unwrap(); + let func_type = self.func_type_at(func_type).unwrap(); let operator_config = self.config.operator_config; - match OperatorValidator::new(func_type, locals, operator_config) { + match OperatorValidator::new(func_type.item, locals, operator_config) { Ok(validator) => self.current_operator_validator = Some(validator), Err(err) => { self.validation_error = Some(ParserState::Error( @@ -772,11 +859,9 @@ impl<'a> ValidatingParser<'a> { } } ParserState::CodeOperator(ref operator) => { - let check = self - .current_operator_validator - .as_mut() - .unwrap() - .process_operator(operator, &self.resources); + let mut validator = self.current_operator_validator.take().unwrap(); + let check = validator.process_operator(operator, self); + self.current_operator_validator = Some(validator); if let Err(err) = check { self.set_operator_validation_error(err); @@ -785,55 +870,68 @@ impl<'a> ValidatingParser<'a> { ParserState::EndFunctionBody => { let check = self .current_operator_validator - .as_ref() + .take() .unwrap() .process_end_function(); if let Err(err) = check { self.set_operator_validation_error(err); } - self.current_func_index += 1; - self.current_operator_validator = None; + self.cur_module_mut().current_func_index += 1; } ParserState::BeginDataSectionEntryBody(_) => { - self.data_found += 1; + self.cur_module_mut().data_found += 1; } ParserState::BeginActiveDataSectionEntry(memory_index) => { - if memory_index as usize >= self.resources.memories.len() { + if self.get_memory(self.def(memory_index)).is_err() { self.set_validation_error( "unknown memory: data section memory index out of bounds", ); } else { - self.init_expression_state = Some(InitExpressionState { - ty: Type::I32, - global_count: self.resources.globals.len(), - function_count: self.resources.func_type_indices.len(), - validated: false, - }); + self.set_init_expression_state(Type::I32); } } ParserState::EndWasm => { - if self.resources.func_type_indices.len() - != self.current_func_index as usize + self.func_nonlocal_count as usize - { + let current_func = self.current_func_index(); + let module = &mut self.cur_module(); + if module.resources.func_type_indices.len() != current_func.item as usize { self.set_validation_error( "function and code section have inconsistent lengths", ); + return; } - if let Some(data_count) = self.resources.data_count { - if data_count != self.data_found { + if let Some(data_count) = module.resources.data_count { + if data_count != module.data_found { self.set_validation_error("data count section and passive data mismatch"); } + return; + } + if self.modules.len() > 1 { + // Pop our nested module from the stack since it's no longer + // needed + self.modules.pop(); + + // Restore the parser back to the previous state + mem::swap( + &mut self.parser, + &mut self.modules.last_mut().unwrap().parser, + ); } } ParserState::ModuleSectionEntry(type_index) => { if !self.config.operator_config.enable_module_linking { self.set_validation_error("module linking proposal not enabled"); - } else if self.resources.module_type_indices.len() >= MAX_WASM_MODULES { + } else if self.cur_module().resources.module_type_indices.len() >= MAX_WASM_MODULES + { self.set_validation_error("modules count out of bounds"); } else { + let type_index = self.def(type_index); match self.module_type_at(type_index) { - Ok(_) => self.resources.module_type_indices.push(type_index), + Ok(_) => self + .cur_module_mut() + .resources + .module_type_indices + .push(type_index), Err(e) => self.validation_error = Some(e), } } @@ -841,16 +939,25 @@ impl<'a> ValidatingParser<'a> { ParserState::BeginInstantiate { module, count } => { if !self.config.operator_config.enable_module_linking { self.set_validation_error("module linking proposal not enabled"); - } else if module as usize >= self.resources.module_type_indices.len() { - self.set_validation_error("module is not defined"); - } else if self.resources.instance_type_indices.len() >= MAX_WASM_INSTANCES { + } else if self.cur_module().resources.instance_type_indices.len() + >= MAX_WASM_INSTANCES + { self.set_validation_error("instance count out of bounds"); } else { - self.resources + let def = self.def(InstanceDef::Instantiated { module_idx: module }); + self.cur_module_mut() + .resources .instance_type_indices - .push(InstanceDef::Instantiated { module_idx: module }); - let module_ty = self.resources.module_type_indices[module as usize]; - if count as usize != self.module_type_at(module_ty).unwrap().imports.len() { + .push(def); + let module_ty = match self.get_module_type_index(self.def(module)) { + Ok(ty) => ty, + Err(e) => { + self.validation_error = Some(e); + return; + } + }; + let ty = self.module_type_at(module_ty).unwrap(); + if count as usize != ty.item.imports.len() { self.set_validation_error("wrong number of imports provided"); } else { self.module_instantiation = Some((module_ty, 0)); @@ -860,8 +967,9 @@ impl<'a> ValidatingParser<'a> { ParserState::InstantiateParameter { kind, index } => { let (module_ty_idx, import_idx) = self.module_instantiation.take().unwrap(); let module_ty = self.module_type_at(module_ty_idx).unwrap(); - let ty = module_ty.imports[import_idx].ty.clone(); - match self.check_instantiate_field(&ty, kind, index) { + let ty = module_ty.item.imports[import_idx].ty.clone(); + let ty = module_ty.map(|_| &ty); + match self.check_instantiate_field(ty, kind, index) { Ok(()) => { self.module_instantiation = Some((module_ty_idx, import_idx + 1)); } @@ -871,26 +979,55 @@ impl<'a> ValidatingParser<'a> { ParserState::EndInstantiate => { let (module_ty, import_idx) = self.module_instantiation.take().unwrap(); let module_ty = self.module_type_at(module_ty).unwrap(); - if import_idx != module_ty.imports.len() { + if import_idx != module_ty.item.imports.len() { self.set_validation_error("not enough imports provided"); } } - ParserState::AliasSectionEntry(ref alias) => match alias.instance { - AliasedInstance::Parent => { - self.set_validation_error("parent instances not supported"); + ParserState::AliasSectionEntry(ref alias) => { + let instance_idx = match alias.instance { + AliasedInstance::Parent => None, + AliasedInstance::Child(instance_idx) => Some(instance_idx), + }; + let (kind, index) = (alias.kind, alias.index); + match self.check_alias_entry(instance_idx, kind, index) { + Ok(()) => {} + Err(e) => self.validation_error = Some(e), } - AliasedInstance::Child(instance_idx) => { - let (kind, index) = (alias.kind, alias.index); - match self.check_alias_entry(instance_idx, kind, index) { - Ok(()) => {} - Err(e) => self.validation_error = Some(e), + } + ParserState::InlineModule(ref module) => { + let parser = match module.module() { + Ok(m) => m, + Err(e) => { + self.validation_error = Some(ParserState::Error(e)); + return; } + }; + self.total_nested_modules += 1; + if self.total_nested_modules > MAX_WASM_MODULES { + self.set_validation_error("too many nested modules"); } - }, + + // Save the state of our parser in our module + let old_parser = mem::replace(&mut self.parser, parser.into()); + self.cur_module_mut().parser = old_parser; + + // Then allocate a child module and push it onto our stack of + // modules we're validating. + self.modules.push(Module::default()); + } _ => (), }; } + fn set_init_expression_state(&mut self, ty: Type) { + self.init_expression_state = Some(InitExpressionState { + ty, + global_count: self.cur_module().resources.globals.len(), + function_count: self.cur_module().resources.func_type_indices.len(), + validated: false, + }); + } + pub fn create_validating_operator_parser<'b>( &mut self, ) -> ValidatorResult> @@ -904,12 +1041,10 @@ impl<'a> ValidatingParser<'a> { self.read(); let operator_validator = match *self.last_state() { ParserState::FunctionBodyLocals { ref locals } => { - let index = (self.current_func_index + self.func_nonlocal_count) as usize; - let func_type = self - .func_type_at(self.resources.func_type_indices[index]) - .unwrap(); + let index = self.current_func_index(); + let func_type = self.func_type_at(self.get_func_type_index(index)?).unwrap(); let operator_config = self.config.operator_config; - OperatorValidator::new(func_type, locals, operator_config) + OperatorValidator::new(func_type.item, locals, operator_config) .map_err(|e| ParserState::Error(e.set_offset(self.read_position.unwrap())))? } _ => panic!("Invalid reader state"), @@ -928,53 +1063,59 @@ impl<'a> ValidatingParser<'a> { fn check_instantiate_field( &mut self, - expected: &ImportSectionEntryType, + expected: Def<&ImportSectionEntryType>, kind: ExternalKind, index: u32, ) -> ValidatorResult<'a, ()> { - self.check_external_kind("referenced", kind, index)?; + let index = self.def(index); let actual = match kind { - ExternalKind::Function => { - let actual_type = self.resources.func_type_indices[index as usize]; - ImportSectionEntryType::Function(actual_type) - } - ExternalKind::Table => { - ImportSectionEntryType::Table(self.resources.tables[index as usize]) - } + ExternalKind::Function => self + .get_func_type_index(index)? + .map(ImportSectionEntryType::Function), + ExternalKind::Table => self.get_table(index)?.map(ImportSectionEntryType::Table), ExternalKind::Memory => { - ImportSectionEntryType::Memory(self.resources.memories[index as usize]) - } - ExternalKind::Global => { - ImportSectionEntryType::Global(self.resources.globals[index as usize]) - } - ExternalKind::Module => { - let actual_type = self.resources.module_type_indices[index as usize]; - ImportSectionEntryType::Module(actual_type) - } - ExternalKind::Instance => match self.resources.instance_type_indices[index as usize] { - InstanceDef::Imported { type_idx } => ImportSectionEntryType::Instance(type_idx), - InstanceDef::Instantiated { module_idx } => { - let expected = match expected { - ImportSectionEntryType::Instance(idx) => idx, - _ => return self.create_error("wrong kind of item used for instantiate"), - }; - let expected = self.instance_type_at(*expected)?; - let actual_ty = self.resources.module_type_indices[module_idx as usize]; - let actual = self.module_type_at(actual_ty)?; - return self.check_export_sets_match(&expected.exports, &actual.exports); + self.def(ImportSectionEntryType::Memory(*self.get_memory(index)?)) + } + ExternalKind::Global => self.get_global(index)?.map(ImportSectionEntryType::Global), + ExternalKind::Module => self + .get_module_type_index(index)? + .map(ImportSectionEntryType::Module), + ExternalKind::Instance => { + let def = self.get_instance_def(index)?; + match def.item { + InstanceDef::Imported { type_idx } => def + .as_ref() + .map(|_| ImportSectionEntryType::Instance(type_idx)), + InstanceDef::Instantiated { module_idx } => { + let expected = match expected.item { + ImportSectionEntryType::Instance(idx) => expected.map(|_| *idx), + _ => { + return self.create_error("wrong kind of item used for instantiate") + } + }; + let expected = self.instance_type_at(expected)?; + let module_idx = def.as_ref().map(|_| module_idx); + let actual = self.get_module_type_index(module_idx)?; + let actual = self.module_type_at(actual)?; + return self.check_export_sets_match( + expected.map(|m| &*m.exports), + actual.map(|m| &*m.exports), + ); + } } - }, + } ExternalKind::Type => return self.create_error("cannot export types"), }; - self.check_imports_match(expected, &actual) + let item = actual.item; + self.check_imports_match(expected, actual.map(|_| &item)) } // Note that this function is basically implementing // https://webassembly.github.io/spec/core/exec/modules.html#import-matching fn check_imports_match( &self, - expected: &ImportSectionEntryType, - actual: &ImportSectionEntryType, + expected: Def<&ImportSectionEntryType>, + actual: Def<&ImportSectionEntryType>, ) -> ValidatorResult<'a, ()> { let limits_match = |expected: &ResizableLimits, actual: &ResizableLimits| { actual.initial >= expected.initial @@ -986,14 +1127,14 @@ impl<'a> ValidatingParser<'a> { None => true, } }; - match (expected, actual) { + match (expected.item, actual.item) { ( - ImportSectionEntryType::Function(expected), - ImportSectionEntryType::Function(actual), + ImportSectionEntryType::Function(expected_idx), + ImportSectionEntryType::Function(actual_idx), ) => { - let expected = self.func_type_at(*expected)?; - let actual = self.func_type_at(*actual)?; - if actual == expected { + let expected = self.func_type_at(expected.map(|_| *expected_idx))?; + let actual = self.func_type_at(actual.map(|_| *actual_idx))?; + if actual.item == expected.item { return Ok(()); } self.create_error("function provided for instantiation has wrong type") @@ -1021,24 +1162,33 @@ impl<'a> ValidatingParser<'a> { self.create_error("global provided for instantiation has wrong type") } ( - ImportSectionEntryType::Instance(expected), - ImportSectionEntryType::Instance(actual), + ImportSectionEntryType::Instance(expected_idx), + ImportSectionEntryType::Instance(actual_idx), ) => { - let expected = self.instance_type_at(*expected)?; - let actual = self.instance_type_at(*actual)?; - self.check_export_sets_match(&expected.exports, &actual.exports)?; + let expected = self.instance_type_at(expected.map(|_| *expected_idx))?; + let actual = self.instance_type_at(actual.map(|_| *actual_idx))?; + self.check_export_sets_match( + expected.map(|i| &*i.exports), + actual.map(|i| &*i.exports), + )?; Ok(()) } - (ImportSectionEntryType::Module(expected), ImportSectionEntryType::Module(actual)) => { - let expected = self.module_type_at(*expected)?; - let actual = self.module_type_at(*actual)?; - if expected.imports.len() != actual.imports.len() { + ( + ImportSectionEntryType::Module(expected_idx), + ImportSectionEntryType::Module(actual_idx), + ) => { + let expected = self.module_type_at(expected.map(|_| *expected_idx))?; + let actual = self.module_type_at(actual.map(|_| *actual_idx))?; + if expected.item.imports.len() != actual.item.imports.len() { return self.create_error("mismatched number of module imports"); } - for (expected, actual) in expected.imports.iter().zip(actual.imports.iter()) { - self.check_imports_match(&expected.ty, &actual.ty)?; + for (a, b) in expected.item.imports.iter().zip(actual.item.imports.iter()) { + self.check_imports_match(expected.map(|_| &a.ty), actual.map(|_| &b.ty))?; } - self.check_export_sets_match(&expected.exports, &actual.exports)?; + self.check_export_sets_match( + expected.map(|i| &*i.exports), + actual.map(|i| &*i.exports), + )?; Ok(()) } _ => self.create_error("wrong kind of item used for instantiate"), @@ -1047,82 +1197,171 @@ impl<'a> ValidatingParser<'a> { fn check_export_sets_match( &self, - expected: &[ExportType<'_>], - actual: &[ExportType<'_>], + expected: Def<&[ExportType<'_>]>, + actual: Def<&[ExportType<'_>]>, ) -> ValidatorResult<'a, ()> { let name_to_idx = actual + .item .iter() .enumerate() .map(|(i, e)| (e.name, i)) .collect::>(); - for expected in expected { - let idx = match name_to_idx.get(expected.name) { + for expected_export in expected.item { + let idx = match name_to_idx.get(expected_export.name) { Some(i) => *i, - None => return self.create_error(&format!("no export named `{}`", expected.name)), + None => { + return self + .create_error(&format!("no export named `{}`", expected_export.name)) + } }; - self.check_imports_match(&expected.ty, &actual[idx].ty)?; + self.check_imports_match( + expected.map(|_| &expected_export.ty), + actual.map(|_| &actual.item[idx].ty), + )?; } Ok(()) } fn check_alias_entry( &mut self, - instance_idx: u32, + instance_idx: Option, kind: ExternalKind, - export_idx: u32, + idx: u32, ) -> ValidatorResult<'a, ()> { - let ty = match self - .resources - .instance_type_indices - .get(instance_idx as usize) - { - Some(ty) => ty, - None => { - return self.create_error("unknown instance: aliased instance index out of bounds"); - } - }; - let exports = match ty { - InstanceDef::Imported { type_idx } => &self.instance_type_at(*type_idx)?.exports, - InstanceDef::Instantiated { module_idx } => { - let ty = self.resources.module_type_indices[*module_idx as usize]; - &self.module_type_at(ty)?.exports + match instance_idx { + Some(instance_idx) => { + let ty = self.get_instance_def(self.def(instance_idx))?; + let exports = match ty.item { + InstanceDef::Imported { type_idx } => { + let ty = self.instance_type_at(ty.as_ref().map(|_| type_idx))?; + ty.map(|t| &t.exports) + } + InstanceDef::Instantiated { module_idx } => { + let ty = self.get_module_type_index(ty.as_ref().map(|_| module_idx))?; + let ty = self.module_type_at(ty)?; + ty.map(|t| &t.exports) + } + }; + let export = match exports.item.get(idx as usize) { + Some(e) => e, + None => { + return self.create_error("aliased export index out of bounds"); + } + }; + match (export.ty, kind) { + (ImportSectionEntryType::Function(ty), ExternalKind::Function) => { + let def = exports.map(|_| ty); + self.cur_module_mut().resources.func_type_indices.push(def); + self.cur_module_mut().func_nonlocal_count += 1; + } + (ImportSectionEntryType::Table(ty), ExternalKind::Table) => { + let def = exports.map(|_| ty); + self.cur_module_mut().resources.tables.push(def); + } + (ImportSectionEntryType::Memory(ty), ExternalKind::Memory) => { + self.cur_module_mut().resources.memories.push(ty); + } + (ImportSectionEntryType::Global(ty), ExternalKind::Global) => { + let def = exports.map(|_| ty); + self.cur_module_mut().resources.globals.push(def); + } + (ImportSectionEntryType::Instance(ty), ExternalKind::Instance) => { + let def = exports.map(|_| InstanceDef::Imported { type_idx: ty }); + self.cur_module_mut() + .resources + .instance_type_indices + .push(def); + } + (ImportSectionEntryType::Module(ty), ExternalKind::Module) => { + let def = exports.map(|_| ty); + self.cur_module_mut() + .resources + .module_type_indices + .push(def); + } + _ => return self.create_error("alias kind mismatch with export kind"), + } } - }; - let export = match exports.get(export_idx as usize) { - Some(e) => e, None => { - return self.create_error("aliased export index out of bounds"); - } - }; - match (export.ty, kind) { - (ImportSectionEntryType::Function(ty), ExternalKind::Function) => { - self.func_nonlocal_count += 1; - self.resources.func_type_indices.push(ty); - } - (ImportSectionEntryType::Table(ty), ExternalKind::Table) => { - self.resources.tables.push(ty); - } - (ImportSectionEntryType::Memory(ty), ExternalKind::Memory) => { - self.resources.memories.push(ty); - } - (ImportSectionEntryType::Global(ty), ExternalKind::Global) => { - self.resources.globals.push(ty); - } - (ImportSectionEntryType::Instance(ty), ExternalKind::Instance) => { - self.resources - .instance_type_indices - .push(InstanceDef::Imported { type_idx: ty }); - } - (ImportSectionEntryType::Module(ty), ExternalKind::Module) => { - self.resources.module_type_indices.push(ty); + let idx = match self.modules.len().checked_sub(2) { + None => return self.create_error("no parent module to alias from"), + Some(module) => Def { module, item: idx }, + }; + match kind { + ExternalKind::Module => { + let ty = self.get_module_type_index(idx)?; + self.cur_module_mut().resources.module_type_indices.push(ty); + } + ExternalKind::Type => { + // make sure this type actually exists, then push it as + // ourselve aliasing that type. + self.get_type(idx)?; + self.cur_module_mut() + .resources + .types + .push(ValidatedType::Alias(idx)); + } + _ => return self.create_error("only parent types/modules can be aliased"), + } } - _ => return self.create_error("alias kind mismatch with export kind"), } Ok(()) } } +impl<'a> WasmModuleResources for ValidatingParser<'a> { + type TypeDef = crate::TypeDef<'a>; + type TableType = crate::TableType; + type MemoryType = crate::MemoryType; + type GlobalType = crate::GlobalType; + + fn type_at(&self, at: u32) -> Option<&Self::TypeDef> { + self.get_type(self.def(at)).ok().map(|t| t.item) + } + + fn table_at(&self, at: u32) -> Option<&Self::TableType> { + self.get_table(self.def(at)).ok().map(|t| &t.item) + } + + fn memory_at(&self, at: u32) -> Option<&Self::MemoryType> { + self.get_memory(self.def(at)).ok() + } + + fn global_at(&self, at: u32) -> Option<&Self::GlobalType> { + self.get_global(self.def(at)).ok().map(|t| &t.item) + } + + fn func_type_at(&self, at: u32) -> Option<&FuncType> { + let ty = self.get_func_type_index(self.def(at)).ok()?; + let ty = self.func_type_at(ty).ok()?; + Some(ty.item) + } + + fn element_type_at(&self, at: u32) -> Option { + self.cur_module() + .resources + .element_types + .get(at as usize) + .cloned() + } + + fn element_count(&self) -> u32 { + self.cur_module().resources.element_types.len() as u32 + } + + fn data_count(&self) -> u32 { + self.cur_module().resources.data_count.unwrap_or(0) + } + + fn is_function_referenced(&self, idx: u32) -> bool { + self.cur_module() + .resources + .function_references + .contains(&idx) + } +} + impl<'a> WasmDecoder<'a> for ValidatingParser<'a> { fn read(&mut self) -> &ParserState<'a> { if self.validation_error.is_some() { @@ -1139,7 +1378,7 @@ impl<'a> WasmDecoder<'a> for ValidatingParser<'a> { ParserInput::SkipSection => panic!("Not supported"), ParserInput::ReadSectionRawData => panic!("Not supported"), ParserInput::SkipFunctionBody => { - self.current_func_index += 1; + self.cur_module_mut().current_func_index += 1; self.parser.push_input(input); } _ => self.parser.push_input(input), @@ -1221,13 +1460,11 @@ impl<'b> ValidatingOperatorParser<'b> { /// let mut parser = ValidatingParser::new(data, None); /// let mut i = 0; /// loop { - /// { - /// match *parser.read() { - /// ParserState::Error(_) | - /// ParserState::EndWasm => break, - /// ParserState::BeginFunctionBody {..} => (), - /// _ => continue - /// } + /// match parser.read() { + /// ParserState::Error(_) | + /// ParserState::EndWasm => break, + /// ParserState::BeginFunctionBody {..} => (), + /// _ => continue /// } /// let mut reader = parser /// .create_validating_operator_parser() @@ -1235,7 +1472,7 @@ impl<'b> ValidatingOperatorParser<'b> { /// println!("Function {}", i); /// i += 1; /// while !reader.eof() { - /// let read = reader.next(parser.get_resources()); + /// let read = reader.next(&parser); /// if let Ok(ref op) = read { /// println!(" {:?}", op); /// } else { @@ -1304,20 +1541,12 @@ pub fn validate_function_body( locals.push((count, ty)); } let operators_reader = function_body.get_operators_reader()?; - let func_type_index = resources - .func_type_id_at(func_index) - // Note: This was an out-of-bounds access before the change to return `Option` - // so I assumed it is considered a bug to access a non-existing function - // id here and went with panicking instead of returning a proper error. - .expect("the function index of the validated function itself is out of bounds"); let func_type = resources - .type_at(func_type_index) + .func_type_at(func_index) // Note: This was an out-of-bounds access before the change to return `Option` // so I assumed it is considered a bug to access a non-existing function // id here and went with panicking instead of returning a proper error. - .expect("the function type indexof the validated function itself is out of bounds") - .as_func() - .unwrap(); + .expect("the function index of the validated function itself is out of bounds"); let mut operator_validator = OperatorValidator::new(func_type, &locals, operator_config) .map_err(|e| e.set_offset(offset))?; let mut eof_found = false; @@ -1364,12 +1593,12 @@ pub fn validate(bytes: &[u8], config: Option) -> Result< let operator_config = config.map(|c| c.operator_config); for (i, range) in func_ranges.into_iter().enumerate() { let function_body = range.slice(bytes); - let function_index = i as u32 + parser.func_nonlocal_count; + let function_index = i as u32 + parser.modules[0].func_nonlocal_count; validate_function_body( function_body, range.start, function_index, - &parser.resources, + &parser, operator_config, )?; } diff --git a/tests/local/module-linking/alias.wast b/tests/local/module-linking/alias.wast index 178d4d259c..986fd5387b 100644 --- a/tests/local/module-linking/alias.wast +++ b/tests/local/module-linking/alias.wast @@ -359,18 +359,20 @@ call $i.$f) ) -(module - (import "" (instance $i (export "a" (func)))) +(assert_invalid + (module + (import "" (instance $i (export "a" (func)))) - (import "" (module $m - (import "" (module (export "a" (func)))) - )) + (import "" (module $m + (import "" (module (export "a" (func)))) + )) - (module $local - (export $i)) + (module $local + (export $i)) - (instance (instantiate $m (module $local))) -) + (instance (instantiate $m (module $local))) + ) + "only parent types/modules can be aliased") (assert_malformed (module quote @@ -584,7 +586,7 @@ "\01" ;; 1 alias "\00\00\00\00" ;; (alias (instance 0) (func 0)) ) - "aliased instance index out of bounds") + "unknown module") (module (import "" (module $m diff --git a/tests/local/module-linking/infer-types.wast b/tests/local/module-linking/infer-types.wast index 78a27ca3ae..60aa56c558 100644 --- a/tests/local/module-linking/infer-types.wast +++ b/tests/local/module-linking/infer-types.wast @@ -48,7 +48,7 @@ (module (module $empty) - (instance $i (instantiate $m (module $m))) + (instance $i (instantiate $m (module $empty))) (export "1" (func $i.$f)) (export "2" (global $i.$g)) (export "3" (table $i.$t)) diff --git a/tests/local/module-linking/instantiate.wast b/tests/local/module-linking/instantiate.wast index ab6dcd4f2b..0c2c46e3cb 100644 --- a/tests/local/module-linking/instantiate.wast +++ b/tests/local/module-linking/instantiate.wast @@ -72,13 +72,13 @@ (module (instance (instantiate 0)) ) - "module is not defined") + "unknown module") (assert_invalid (module (import "" (module)) (instance (instantiate 1)) ) - "module is not defined") + "unknown module") (assert_invalid (module @@ -254,4 +254,4 @@ (module (instance (instantiate 0)) ) - "module is not defined") + "unknown module") diff --git a/tests/local/module-linking/nested-modules.wast b/tests/local/module-linking/nested-modules.wast index 96abb6fb29..d89bc24b86 100644 --- a/tests/local/module-linking/nested-modules.wast +++ b/tests/local/module-linking/nested-modules.wast @@ -29,3 +29,13 @@ (import "" (module)) (type (module)) ) + +;; be sure to typecheck nested modules +(assert_invalid + (module + (module + (func + i32.add) + ) + ) + "type mismatch") diff --git a/tests/local/module-linking/types.wast b/tests/local/module-linking/types.wast index f02771f8e5..1825191a66 100644 --- a/tests/local/module-linking/types.wast +++ b/tests/local/module-linking/types.wast @@ -57,3 +57,14 @@ )) ) "type index is not a func") + +(assert_invalid + (module + (export "" (module 0)) + ) + "exported module index out of bounds") +(assert_invalid + (module + (export "" (instance 0)) + ) + "exported instance index out of bounds") diff --git a/tests/local/module-linking/virtualize.wast b/tests/local/module-linking/virtualize.wast index fdd21c39db..0a92c1484f 100644 --- a/tests/local/module-linking/virtualize.wast +++ b/tests/local/module-linking/virtualize.wast @@ -57,7 +57,11 @@ (import "wasi_file" (instance $wasi-file (type $WasiFile))) (alias $wasi-file.$read (instance $wasi-file) (func 0)) (func $play (export "play") + i32.const 0 + i32.const 0 + i32.const 0 call $wasi-file.$read + drop ) ) From f38240c44929b701baba65cfd42f62e2f357b222 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 29 Jun 2020 10:30:19 -0700 Subject: [PATCH 2/2] Fix top-level `validate` function This was previously trying to validate all functions in the context of the top-level module, when instead it needed to validate each function within the right module. --- crates/wasmparser/src/validator.rs | 6 ++++-- tests/roundtrip.rs | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index d273c94179..d8fbe2b317 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -1584,8 +1584,10 @@ pub fn validate(bytes: &[u8], config: Option) -> Result< ParserState::EndWasm => break, ParserState::Error(ref e) => return Err(e.clone()), ParserState::BeginFunctionBody { range } => { - parser_input = Some(ParserInput::SkipFunctionBody); - func_ranges.push(range); + if parser.modules.len() == 1 { + parser_input = Some(ParserInput::SkipFunctionBody); + func_ranges.push(range); + } } _ => (), } diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 6c9901ef50..5a3ed63a0d 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -457,6 +457,9 @@ impl TestState { bail!("Max iterations exceeded"); } } + + // Also test the top-level validation function + wasmparser::validate(contents, Some(config))?; self.bump_ntests(); Ok(()) }