From a43d4bacd319e0e72f543727ef0ab55ec37f62a7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 17:57:12 +0100 Subject: [PATCH 01/35] initial traits and impls for new validation interface --- src/operators_validator.rs | 145 +++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index cb1dd1a5..66e81ec6 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -253,16 +253,161 @@ enum BlockType { If, } +/// Types that quality as Wasm types for validation purposes. +/// +/// Must be comparable with `wasmparser` given Wasm types and +/// must be comparable to themselves. +pub trait WasmType: PartialEq + PartialEq + Eq {} + +/// Types that qualify as Wasm function types for validation purposes. +pub trait WasmFuncType { + /// A type that is comparable with Wasm types. + type Type: WasmType; + + /// Returns the number of input types. + fn len_inputs(&self) -> usize; + /// Returns the number of output types. + fn len_outputs(&self) -> usize; + /// Returns the type at given index if any. + /// + /// # Note + /// + /// The returned type may be wrapped by the user crate and thus + /// the actually returned type only has to be comparable to a Wasm type. + fn input_at(&self, at: u32) -> Option<&Self::Type>; + /// Returns the type at given index if any. + /// + /// # Note + /// + /// The returned type may be wrapped by the user crate and thus + /// the actually returned type only has to be comparable to a Wasm type. + fn output_at(&self, at: u32) -> Option<&Self::Type>; +} + +/// Types that qualify as Wasm table types for validation purposes. +pub trait WasmTableType { + /// A type that is comparable with Wasm types. + type Type: WasmType; + + /// Returns the element type of the table. + fn element_type(&self) -> &Self::Type; + /// Returns the initial limit of the table. + fn initial_limit(&self) -> u32; + /// Returns the maximum limit of the table if any. + fn maximum_limit(&self) -> Option; +} + +/// Types that qualify as Wasm memory types for validation purposes. +pub trait WasmMemoryType { + /// Returns `true` if the linear memory is shared. + fn is_shared(&self) -> bool; + /// Returns the initial limit of the linear memory. + fn initial_limit(&self) -> u32; + /// Returns the maximum limit of the linear memory if any. + fn maximum_limit(&self) -> Option; +} + +/// Types that qualify as Wasm global types for validation purposes. +pub trait WasmGlobalType { + /// A type that is comparable with Wasm types. + type Type: WasmType; + + /// Returns `true` if the global variable is mutable. + fn is_mutable(&self) -> bool; + /// Returns the content type of the global variable. + fn content_type(&self) -> &Self::Type; +} + pub trait WasmModuleResources { + type FuncType: WasmFuncType; + type TableType: WasmTableType; + type MemoryType: WasmMemoryType; + type GlobalType: WasmGlobalType; + + fn type_at(&self, at: u32) -> &[Self::FuncType]; + fn table_at(&self, at: u32) -> &[Self::TableType]; + fn memory_at(&self, at: u32) -> &[Self::MemoryType]; + fn global_at(&self, at: u32) -> &[Self::GlobalType]; + fn signature_id_at(&self, at: u32) -> &[u32]; + fn types(&self) -> &[FuncType]; fn tables(&self) -> &[TableType]; fn memories(&self) -> &[MemoryType]; fn globals(&self) -> &[GlobalType]; fn func_type_indices(&self) -> &[u32]; + fn element_count(&self) -> u32; fn data_count(&self) -> u32; } +pub trait DefaultWasmModuleResources: WasmModuleResources< + FuncType = crate::FuncType, + TableType = crate::TableType, + MemoryType = crate::MemoryType, + GlobalType = crate::GlobalType, +> {} + +impl WasmFuncType for crate::FuncType { + type Type = crate::Type; + + fn len_inputs(&self) -> usize { + self.params.len() + } + + fn len_outputs(&self) -> usize { + self.returns.len() + } + + fn input_at(&self, at: u32) -> Option<&Self::Type> { + self.params.get(at as usize) + } + + fn output_at(&self, at: u32) -> Option<&Self::Type> { + self.returns.get(at as usize) + } +} + +impl WasmGlobalType for crate::GlobalType { + type Type = crate::Type; + + fn is_mutable(&self) -> bool { + self.mutable + } + + fn content_type(&self) -> &Self::Type { + &self.content_type + } +} + +impl WasmTableType for crate::TableType { + type Type = crate::Type; + + fn element_type(&self) -> &Self::Type { + &self.element_type + } + + fn initial_limit(&self) -> u32 { + self.limits.initial + } + + fn maximum_limit(&self) -> Option { + self.limits.maximum + } +} + +impl WasmMemoryType for crate::MemoryType { + fn is_shared(&self) -> bool { + self.shared + } + + fn initial_limit(&self) -> u32 { + self.limits.initial + } + fn maximum_limit(&self) -> Option { + self.limits.maximum + } +} + pub enum FunctionEnd { No, Yes, From 113bcab6d7b0db72e664cf1fcbe97bcb7a8878b0 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 18:18:28 +0100 Subject: [PATCH 02/35] implement new validation interface into existing validation procedures --- src/lib.rs | 1 + src/operators_validator.rs | 116 +++++++++++++++++++++++++++++-------- src/validator.rs | 58 +++++++++++++++++-- 3 files changed, 146 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4d763e97..a63e912a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,7 @@ pub use crate::validator::ValidatingParserConfig; pub use crate::operators_validator::OperatorValidatorConfig; pub use crate::operators_validator::WasmModuleResources; +pub use crate::operators_validator::DefaultWasmModuleResources; pub use crate::readers::CodeSectionReader; pub use crate::readers::CustomSectionContent; diff --git a/src/operators_validator.rs b/src/operators_validator.rs index b7f03518..11d744b9 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -122,11 +122,16 @@ impl FuncState { } Ok(()) } - fn push_block( + fn push_block( &mut self, ty: TypeOrFuncType, block_type: BlockType, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, ) -> OperatorValidatorResult<()> { let (start_types, return_types) = match ty { TypeOrFuncType::Type(Type::EmptyBlockType) => (vec![], vec![]), @@ -324,11 +329,11 @@ pub trait WasmModuleResources { type MemoryType: WasmMemoryType; type GlobalType: WasmGlobalType; - fn type_at(&self, at: u32) -> &[Self::FuncType]; - fn table_at(&self, at: u32) -> &[Self::TableType]; - fn memory_at(&self, at: u32) -> &[Self::MemoryType]; - fn global_at(&self, at: u32) -> &[Self::GlobalType]; - fn signature_id_at(&self, at: u32) -> &[u32]; + fn type_at(&self, at: u32) -> &Self::FuncType; + fn table_at(&self, at: u32) -> &Self::TableType; + fn memory_at(&self, at: u32) -> &Self::MemoryType; + fn global_at(&self, at: u32) -> &Self::GlobalType; + fn signature_id_at(&self, at: u32) -> u32; fn types(&self) -> &[FuncType]; fn tables(&self) -> &[TableType]; @@ -340,12 +345,17 @@ pub trait WasmModuleResources { fn data_count(&self) -> u32; } -pub trait DefaultWasmModuleResources: WasmModuleResources< +pub trait DefaultWasmModuleResources: + WasmModuleResources< FuncType = crate::FuncType, TableType = crate::TableType, MemoryType = crate::MemoryType, GlobalType = crate::GlobalType, -> {} +> +{ +} + +impl WasmType for crate::Type {} impl WasmFuncType for crate::FuncType { type Type = crate::Type; @@ -608,10 +618,20 @@ impl OperatorValidator { Ok(()) } - fn check_memory_index( + fn check_memory_index< + F: WasmFuncType, + T: WasmTableType, + M: WasmMemoryType, + G: WasmGlobalType, + >( &self, memory_index: u32, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, ) -> OperatorValidatorResult<()> { if memory_index as usize >= resources.memories().len() { return Err("no linear memories are present"); @@ -619,10 +639,20 @@ impl OperatorValidator { Ok(()) } - fn check_shared_memory_index( + fn check_shared_memory_index< + F: WasmFuncType, + T: WasmTableType, + M: WasmMemoryType, + G: WasmGlobalType, + >( &self, memory_index: u32, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, ) -> OperatorValidatorResult<()> { if memory_index as usize >= resources.memories().len() { return Err("no linear memories are present"); @@ -633,11 +663,16 @@ impl OperatorValidator { Ok(()) } - fn check_memarg( + fn check_memarg( &self, memarg: &MemoryImmediate, max_align: u32, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, ) -> OperatorValidatorResult<()> { self.check_memory_index(0, resources)?; let align = memarg.flags; @@ -689,10 +724,20 @@ impl OperatorValidator { Ok(()) } - fn check_shared_memarg_wo_align( + fn check_shared_memarg_wo_align< + F: WasmFuncType, + T: WasmTableType, + M: WasmMemoryType, + G: WasmGlobalType, + >( &self, _: &MemoryImmediate, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, ) -> OperatorValidatorResult<()> { self.check_shared_memory_index(0, resources)?; Ok(()) @@ -705,10 +750,15 @@ impl OperatorValidator { Ok(()) } - fn check_block_type( + fn check_block_type( &self, ty: TypeOrFuncType, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, ) -> OperatorValidatorResult<()> { match ty { TypeOrFuncType::Type(Type::EmptyBlockType) @@ -743,10 +793,20 @@ impl OperatorValidator { } } - fn check_block_params( + fn check_block_params< + F: WasmFuncType, + T: WasmTableType, + M: WasmMemoryType, + G: WasmGlobalType, + >( &self, ty: TypeOrFuncType, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, skip: usize, ) -> OperatorValidatorResult<()> { if let TypeOrFuncType::FuncType(idx) = ty { @@ -800,10 +860,20 @@ impl OperatorValidator { Ok(Some(ty)) } - pub(crate) fn process_operator( + pub(crate) fn process_operator< + F: WasmFuncType, + T: WasmTableType, + M: WasmMemoryType, + G: WasmGlobalType, + >( &mut self, operator: &Operator, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, ) -> OperatorValidatorResult { if self.func_state.end_function { return Err("unexpected operator"); diff --git a/src/validator.rs b/src/validator.rs index d1de5c9f..559a8ab2 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -32,8 +32,9 @@ use crate::primitives::{ use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder}; use crate::operators_validator::{ - is_subtype_supertype, FunctionEnd, OperatorValidator, OperatorValidatorConfig, - WasmModuleResources, DEFAULT_OPERATOR_VALIDATOR_CONFIG, + is_subtype_supertype, DefaultWasmModuleResources, FunctionEnd, OperatorValidator, + OperatorValidatorConfig, WasmFuncType, WasmGlobalType, WasmMemoryType, WasmModuleResources, + WasmTableType, DEFAULT_OPERATOR_VALIDATOR_CONFIG, }; use crate::{ElemSectionEntryTable, ElementItem}; @@ -104,7 +105,34 @@ struct ValidatingParserResources { func_type_indices: Vec, } +impl DefaultWasmModuleResources for ValidatingParserResources {} + impl<'a> WasmModuleResources for ValidatingParserResources { + type FuncType = crate::FuncType; + type TableType = crate::TableType; + type MemoryType = crate::MemoryType; + type GlobalType = crate::GlobalType; + + fn type_at(&self, at: u32) -> &Self::FuncType { + &self.types[at as usize] + } + + fn table_at(&self, at: u32) -> &Self::TableType { + &self.tables[at as usize] + } + + fn memory_at(&self, at: u32) -> &Self::MemoryType { + &self.memories[at as usize] + } + + fn global_at(&self, at: u32) -> &Self::GlobalType { + &self.globals[at as usize] + } + + fn signature_id_at(&self, at: u32) -> u32 { + self.func_type_indices[at as usize] + } + fn types(&self) -> &[FuncType] { &self.types } @@ -175,7 +203,7 @@ impl<'a> ValidatingParser<'a> { } } - pub fn get_resources(&self) -> &dyn WasmModuleResources { + pub fn get_resources(&self) -> &dyn DefaultWasmModuleResources { &self.resources } @@ -775,7 +803,15 @@ impl<'b> ValidatingOperatorParser<'b> { /// } /// } /// ``` - pub fn next<'c>(&mut self, resources: &dyn WasmModuleResources) -> Result> + pub fn next<'c, F: WasmFuncType, T: WasmTableType, M: WasmMemoryType, G: WasmGlobalType>( + &mut self, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, + ) -> Result> where 'b: 'c, { @@ -804,11 +840,21 @@ impl<'b> ValidatingOperatorParser<'b> { /// Test whether the given buffer contains a valid WebAssembly function. /// The resources parameter contains all needed data to validate the operators. -pub fn validate_function_body( +pub fn validate_function_body< + F: WasmFuncType, + T: WasmTableType, + M: WasmMemoryType, + G: WasmGlobalType, +>( bytes: &[u8], offset: usize, func_index: u32, - resources: &dyn WasmModuleResources, + resources: &dyn WasmModuleResources< + FuncType = F, + TableType = T, + MemoryType = M, + GlobalType = G, + >, operator_config: Option, ) -> Result<()> { let operator_config = operator_config.unwrap_or(DEFAULT_OPERATOR_VALIDATOR_CONFIG); From d81476c38db4c612c3a4ea947de516bb8893a7cb Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 20:47:06 +0100 Subject: [PATCH 03/35] add len_* methods to WasmModuleResources trait --- src/operators_validator.rs | 9 +++++++++ src/validator.rs | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 11d744b9..d74f5386 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -335,6 +335,15 @@ pub trait WasmModuleResources { fn global_at(&self, at: u32) -> &Self::GlobalType; fn signature_id_at(&self, at: u32) -> u32; + /// Returns the number of types. + fn len_types(&self) -> usize; + /// Returns the number of tables. + fn len_tables(&self) -> usize; + /// Returns the number of linear memories. + fn len_memories(&self) -> usize; + /// Returns the number of global variables. + fn len_globals(&self) -> usize; + fn types(&self) -> &[FuncType]; fn tables(&self) -> &[TableType]; fn memories(&self) -> &[MemoryType]; diff --git a/src/validator.rs b/src/validator.rs index 559a8ab2..337f655b 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -133,6 +133,22 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.func_type_indices[at as usize] } + fn len_types(&self) -> usize { + self.types.len() + } + + fn len_tables(&self) -> usize { + self.tables.len() + } + + fn len_memories(&self) -> usize { + self.memories.len() + } + + fn len_globals(&self) -> usize { + self.globals.len() + } + fn types(&self) -> &[FuncType] { &self.types } From 19e708463117b8387a121b2111237a953b550ea8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 20:47:32 +0100 Subject: [PATCH 04/35] add docs --- src/operators_validator.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index d74f5386..5247b8e2 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -324,15 +324,24 @@ pub trait WasmGlobalType { } pub trait WasmModuleResources { + /// The function type used for validation. type FuncType: WasmFuncType; + /// The table type used for validation. type TableType: WasmTableType; + /// The memory type used for validation. type MemoryType: WasmMemoryType; + /// The global type used for validation. type GlobalType: WasmGlobalType; + /// Returns the type at given index. fn type_at(&self, at: u32) -> &Self::FuncType; + /// Returns the table at given index. fn table_at(&self, at: u32) -> &Self::TableType; + /// Returns the linear memory at given index. fn memory_at(&self, at: u32) -> &Self::MemoryType; + /// Returns the global variable at given index. fn global_at(&self, at: u32) -> &Self::GlobalType; + /// Returns the function signature ID at given index. fn signature_id_at(&self, at: u32) -> u32; /// Returns the number of types. From 447e548606ac2f5640e9392e222ae7a29f67aa81 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 20:47:55 +0100 Subject: [PATCH 05/35] extend WasmType trait (adds compat method) --- src/operators_validator.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 5247b8e2..fef833c3 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -262,7 +262,14 @@ enum BlockType { /// /// Must be comparable with `wasmparser` given Wasm types and /// must be comparable to themselves. -pub trait WasmType: PartialEq + PartialEq + Eq {} +pub trait WasmType: PartialEq + PartialEq + Eq { + /// Converts the custom type into a `wasmparser` known type. + /// + /// # Note + /// + /// This interface is required as bridge until transitioning is complete. + fn to_parser_type(&self) -> crate::Type; +} /// Types that qualify as Wasm function types for validation purposes. pub trait WasmFuncType { @@ -373,7 +380,11 @@ pub trait DefaultWasmModuleResources: { } -impl WasmType for crate::Type {} +impl WasmType for crate::Type { + fn to_parser_type(&self) -> crate::Type { + *self + } +} impl WasmFuncType for crate::FuncType { type Type = crate::Type; From d3cf6def69c49472cc4eb806debf36f22a9bba26 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 20:48:15 +0100 Subject: [PATCH 06/35] remove globals method from WasmModuleResources trait --- src/operators_validator.rs | 15 +++++++-------- src/validator.rs | 4 ---- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index fef833c3..50b9ee44 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -363,7 +363,6 @@ pub trait WasmModuleResources { fn types(&self) -> &[FuncType]; fn tables(&self) -> &[TableType]; fn memories(&self) -> &[MemoryType]; - fn globals(&self) -> &[GlobalType]; fn func_type_indices(&self) -> &[u32]; fn element_count(&self) -> u32; @@ -1040,21 +1039,21 @@ impl OperatorValidator { self.func_state.change_frame_with_type(1, local_type)?; } Operator::GlobalGet { global_index } => { - if global_index as usize >= resources.globals().len() { + if global_index as usize >= resources.len_globals() { return Err("global index out of bounds"); } - let ty = &resources.globals()[global_index as usize]; - self.func_state.change_frame_with_type(0, ty.content_type)?; + let ty = &resources.global_at(global_index); + self.func_state.change_frame_with_type(0, ty.content_type().to_parser_type())?; } Operator::GlobalSet { global_index } => { - if global_index as usize >= resources.globals().len() { + if global_index as usize >= resources.len_globals() { return Err("global index out of bounds"); } - let ty = &resources.globals()[global_index as usize]; - if !ty.mutable { + let ty = &resources.global_at(global_index); + if !ty.is_mutable() { return Err("global expected to be mutable"); } - self.check_operands_1(ty.content_type)?; + self.check_operands_1(ty.content_type().to_parser_type())?; self.func_state.change_frame(1)?; } Operator::I32Load { ref memarg } => { diff --git a/src/validator.rs b/src/validator.rs index 337f655b..5c6551bd 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -161,10 +161,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { &self.memories } - fn globals(&self) -> &[GlobalType] { - &self.globals - } - fn func_type_indices(&self) -> &[u32] { &self.func_type_indices } From 71000b879b605d91ae22d963e0a885333bdc1aad Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 20:49:47 +0100 Subject: [PATCH 07/35] remove memories method from WasmModuleResources trait --- src/operators_validator.rs | 7 +++---- src/validator.rs | 4 ---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 50b9ee44..2acbebab 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -362,7 +362,6 @@ pub trait WasmModuleResources { fn types(&self) -> &[FuncType]; fn tables(&self) -> &[TableType]; - fn memories(&self) -> &[MemoryType]; fn func_type_indices(&self) -> &[u32]; fn element_count(&self) -> u32; @@ -661,7 +660,7 @@ impl OperatorValidator { GlobalType = G, >, ) -> OperatorValidatorResult<()> { - if memory_index as usize >= resources.memories().len() { + if memory_index as usize >= resources.len_memories() { return Err("no linear memories are present"); } Ok(()) @@ -682,10 +681,10 @@ impl OperatorValidator { GlobalType = G, >, ) -> OperatorValidatorResult<()> { - if memory_index as usize >= resources.memories().len() { + if memory_index as usize >= resources.len_memories() { return Err("no linear memories are present"); } - if !resources.memories()[memory_index as usize].shared { + if !resources.memory_at(memory_index).is_shared() { return Err("atomic accesses require shared memory"); } Ok(()) diff --git a/src/validator.rs b/src/validator.rs index 5c6551bd..27627a07 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -157,10 +157,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { &self.tables } - fn memories(&self) -> &[MemoryType] { - &self.memories - } - fn func_type_indices(&self) -> &[u32] { &self.func_type_indices } From fa4cf1d0bd8a555adf20899ba733a7d58ee8292a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 20:54:49 +0100 Subject: [PATCH 08/35] add table_at_checked to WasmModuleResources trait --- src/operators_validator.rs | 2 ++ src/validator.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 2acbebab..8208d1d6 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -344,6 +344,8 @@ pub trait WasmModuleResources { fn type_at(&self, at: u32) -> &Self::FuncType; /// Returns the table at given index. fn table_at(&self, at: u32) -> &Self::TableType; + /// Returns the table at given index if any. + fn table_at_checked(&self, at: u32) -> Option<&Self::TableType>; /// Returns the linear memory at given index. fn memory_at(&self, at: u32) -> &Self::MemoryType; /// Returns the global variable at given index. diff --git a/src/validator.rs b/src/validator.rs index 27627a07..aee3dd7c 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -121,6 +121,10 @@ impl<'a> WasmModuleResources for ValidatingParserResources { &self.tables[at as usize] } + fn table_at_checked(&self, at: u32) -> Option<&Self::TableType> { + self.tables.get(at as usize) + } + fn memory_at(&self, at: u32) -> &Self::MemoryType { &self.memories[at as usize] } From f068385e71ca4fe5f46fbdc13951665c7828e6f9 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 20:55:05 +0100 Subject: [PATCH 09/35] remove tables method from WasmModuleResources trait --- src/operators_validator.rs | 27 +++++++++++++-------------- src/validator.rs | 4 ---- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 8208d1d6..57d4c36c 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -363,7 +363,6 @@ pub trait WasmModuleResources { fn len_globals(&self) -> usize; fn types(&self) -> &[FuncType]; - fn tables(&self) -> &[TableType]; fn func_type_indices(&self) -> &[u32]; fn element_count(&self) -> u32; @@ -990,7 +989,7 @@ impl OperatorValidator { .change_frame_with_types(ty.params.len(), &ty.returns)?; } Operator::CallIndirect { index, table_index } => { - if table_index as usize >= resources.tables().len() { + if table_index as usize >= resources.len_tables() { return Err("table index out of bounds"); } if index as usize >= resources.types().len() { @@ -1941,7 +1940,7 @@ impl OperatorValidator { if table > 0 { self.check_reference_types_enabled()?; } - if table as usize >= resources.tables().len() { + if table as usize >= resources.len_tables() { return Err("table index out of bounds"); } self.check_operands(&[Type::I32, Type::I32, Type::I32])?; @@ -1961,8 +1960,8 @@ impl OperatorValidator { if src_table > 0 || dst_table > 0 { self.check_reference_types_enabled()?; } - if src_table as usize >= resources.tables().len() - || dst_table as usize >= resources.tables().len() + if src_table as usize >= resources.len_tables() + || dst_table as usize >= resources.len_tables() { return Err("table index out of bounds"); } @@ -1971,8 +1970,8 @@ impl OperatorValidator { } Operator::TableGet { table } => { self.check_reference_types_enabled()?; - let ty = match resources.tables().get(table as usize) { - Some(ty) => ty.element_type, + let ty = match resources.table_at_checked(table) { + Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; self.check_operands(&[Type::I32])?; @@ -1980,8 +1979,8 @@ impl OperatorValidator { } Operator::TableSet { table } => { self.check_reference_types_enabled()?; - let ty = match resources.tables().get(table as usize) { - Some(ty) => ty.element_type, + let ty = match resources.table_at_checked(table) { + Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; self.check_operands(&[Type::I32, ty])?; @@ -1989,8 +1988,8 @@ impl OperatorValidator { } Operator::TableGrow { table } => { self.check_reference_types_enabled()?; - let ty = match resources.tables().get(table as usize) { - Some(ty) => ty.element_type, + let ty = match resources.table_at_checked(table) { + Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; self.check_operands(&[ty, Type::I32])?; @@ -1998,15 +1997,15 @@ impl OperatorValidator { } Operator::TableSize { table } => { self.check_reference_types_enabled()?; - if table as usize >= resources.tables().len() { + if table as usize >= resources.len_tables() { return Err("table index out of bounds"); } self.func_state.change_frame_with_type(0, Type::I32)?; } Operator::TableFill { table } => { self.check_bulk_memory_enabled()?; - let ty = match resources.tables().get(table as usize) { - Some(ty) => ty.element_type, + let ty = match resources.table_at_checked(table) { + Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; self.check_operands(&[Type::I32, ty, Type::I32])?; diff --git a/src/validator.rs b/src/validator.rs index aee3dd7c..70d9e159 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -157,10 +157,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { &self.types } - fn tables(&self) -> &[TableType] { - &self.tables - } - fn func_type_indices(&self) -> &[u32] { &self.func_type_indices } From 703456f62410db77be366b53c535639c67cb0ff4 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 21:00:52 +0100 Subject: [PATCH 10/35] add len_func_type_id and remove func_type_indices methods in WasmModuleResources trait --- src/operators_validator.rs | 11 ++++++----- src/validator.rs | 12 ++++++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 57d4c36c..7a45d1b9 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -351,7 +351,7 @@ pub trait WasmModuleResources { /// Returns the global variable at given index. fn global_at(&self, at: u32) -> &Self::GlobalType; /// Returns the function signature ID at given index. - fn signature_id_at(&self, at: u32) -> u32; + fn func_type_id_at(&self, at: u32) -> u32; /// Returns the number of types. fn len_types(&self) -> usize; @@ -361,9 +361,10 @@ pub trait WasmModuleResources { fn len_memories(&self) -> usize; /// Returns the number of global variables. fn len_globals(&self) -> usize; + /// Returns the number of function type indices. + fn len_func_type_id(&self) -> usize; fn types(&self) -> &[FuncType]; - fn func_type_indices(&self) -> &[u32]; fn element_count(&self) -> u32; fn data_count(&self) -> u32; @@ -979,10 +980,10 @@ impl OperatorValidator { self.func_state.start_dead_code() } Operator::Call { function_index } => { - if function_index as usize >= resources.func_type_indices().len() { + if function_index as usize >= resources.len_func_type_id() { return Err("function index out of bounds"); } - let type_index = resources.func_type_indices()[function_index as usize]; + let type_index = resources.func_type_id_at(function_index); let ty = &resources.types()[type_index as usize]; self.check_operands(&ty.params)?; self.func_state @@ -1586,7 +1587,7 @@ impl OperatorValidator { } Operator::RefFunc { function_index } => { self.check_reference_types_enabled()?; - if function_index as usize >= resources.func_type_indices().len() { + if function_index as usize >= resources.len_func_type_id() { return Err("function index out of bounds"); } self.func_state.change_frame_with_type(0, Type::AnyFunc)?; diff --git a/src/validator.rs b/src/validator.rs index 70d9e159..bb6f2616 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -133,7 +133,7 @@ impl<'a> WasmModuleResources for ValidatingParserResources { &self.globals[at as usize] } - fn signature_id_at(&self, at: u32) -> u32 { + fn func_type_id_at(&self, at: u32) -> u32 { self.func_type_indices[at as usize] } @@ -153,12 +153,12 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.globals.len() } - fn types(&self) -> &[FuncType] { - &self.types + fn len_func_type_id(&self) -> usize { + self.func_type_indices.len() } - fn func_type_indices(&self) -> &[u32] { - &self.func_type_indices + fn types(&self) -> &[FuncType] { + &self.types } fn element_count(&self) -> u32 { @@ -895,7 +895,7 @@ pub fn validate_function_body< locals.push((count, ty)); } let operators_reader = function_body.get_operators_reader()?; - let func_type_index = resources.func_type_indices()[func_index as usize]; + let func_type_index = resources.func_type_id_at(func_index); let func_type = &resources.types()[func_type_index as usize]; let mut operator_validator = OperatorValidator::new(func_type, &locals, operator_config); let mut eof_found = false; From b4e613f7516440381da4872bb4a38bb0eeab9552 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 21:01:25 +0100 Subject: [PATCH 11/35] add docs --- src/operators_validator.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 7a45d1b9..6af7bf4a 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -366,7 +366,9 @@ pub trait WasmModuleResources { fn types(&self) -> &[FuncType]; + /// Returns the number of elements. fn element_count(&self) -> u32; + /// Returns the number of bytes in the Wasm data section. fn data_count(&self) -> u32; } From 81bdced5d3cc90d8a78595dfe55839fa208fe9b4 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 22:26:02 +0100 Subject: [PATCH 12/35] remove types method from WasmModuleResources trait --- src/operators_validator.rs | 225 +++++++++++++++++++++++++------------ src/validator.rs | 6 +- 2 files changed, 152 insertions(+), 79 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 6af7bf4a..9eafdfc4 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -17,10 +17,7 @@ use std::cmp::min; use std::result; use std::str; -use crate::primitives::{ - FuncType, GlobalType, MemoryImmediate, MemoryType, Operator, SIMDLaneIndex, TableType, Type, - TypeOrFuncType, -}; +use crate::primitives::{MemoryImmediate, Operator, SIMDLaneIndex, Type, TypeOrFuncType}; /// Test if `subtype` is a subtype of `supertype`. pub(crate) fn is_subtype_supertype(subtype: Type, supertype: Type) -> bool { @@ -137,8 +134,15 @@ impl FuncState { TypeOrFuncType::Type(Type::EmptyBlockType) => (vec![], vec![]), TypeOrFuncType::Type(ty) => (vec![], vec![ty]), TypeOrFuncType::FuncType(idx) => { - let ty = &resources.types()[idx as usize]; - (ty.params.clone().into_vec(), ty.returns.clone().into_vec()) + let ty = resources.type_at(idx); + ( + wasm_func_type_inputs(ty) + .map(WasmType::to_parser_type) + .collect::>(), + wasm_func_type_outputs(ty) + .map(WasmType::to_parser_type) + .collect::>(), + ) } }; if block_type == BlockType::If { @@ -207,16 +211,16 @@ impl FuncState { self.stack_types.push(ty); Ok(()) } - fn change_frame_with_types( + fn change_frame_with_types( &mut self, remove_count: usize, - new_items: &[Type], - ) -> OperatorValidatorResult<()> { + new_items: I, + ) -> OperatorValidatorResult<()> + where + I: Iterator, + { self.remove_frame_stack_types(remove_count)?; - if new_items.is_empty() { - return Ok(()); - } - self.stack_types.extend_from_slice(new_items); + self.stack_types.extend(new_items); Ok(()) } fn change_frame_to_exact_types_from(&mut self, depth: usize) -> OperatorValidatorResult<()> { @@ -286,14 +290,50 @@ pub trait WasmFuncType { /// /// The returned type may be wrapped by the user crate and thus /// the actually returned type only has to be comparable to a Wasm type. - fn input_at(&self, at: u32) -> Option<&Self::Type>; + fn input_at(&self, at: u32) -> Option<&Self::Type>; /// Returns the type at given index if any. /// /// # Note /// /// The returned type may be wrapped by the user crate and thus /// the actually returned type only has to be comparable to a Wasm type. - fn output_at(&self, at: u32) -> Option<&Self::Type>; + fn output_at(&self, at: u32) -> Option<&Self::Type>; +} + +/// Returns an iterator over the input types of a Wasm function type. +fn wasm_func_type_inputs<'a, F, T>(func_type: &'a F) -> impl ExactSizeIterator +where + F: WasmFuncType, + T: WasmType + 'a, +{ + // Quick'n dirty implementation. + // We might create an actual custom iterator + // type if performance issues arise. + let mut result = Vec::new(); + let mut n = 0; + while let Some(ty) = func_type.input_at(n) { + result.push(ty); + n += 1; + } + result.into_iter() +} + +/// Returns an iterator over the output types of a Wasm function type. +fn wasm_func_type_outputs<'a, F, T>(func_type: &'a F) -> impl ExactSizeIterator +where + F: WasmFuncType, + T: WasmType + 'a, +{ + // Quick'n dirty implementation. + // We might create an actual custom iterator + // type if performance issues arise. + let mut result = Vec::new(); + let mut n = 0; + while let Some(ty) = func_type.output_at(n) { + result.push(ty); + n += 1; + } + result.into_iter() } /// Types that qualify as Wasm table types for validation purposes. @@ -364,8 +404,6 @@ pub trait WasmModuleResources { /// Returns the number of function type indices. fn len_func_type_id(&self) -> usize; - fn types(&self) -> &[FuncType]; - /// Returns the number of elements. fn element_count(&self) -> u32; /// Returns the number of bytes in the Wasm data section. @@ -399,11 +437,11 @@ impl WasmFuncType for crate::FuncType { self.returns.len() } - fn input_at(&self, at: u32) -> Option<&Self::Type> { + fn input_at(&self, at: u32) -> Option<&Self::Type> { self.params.get(at as usize) } - fn output_at(&self, at: u32) -> Option<&Self::Type> { + fn output_at(&self, at: u32) -> Option<&Self::Type> { self.returns.get(at as usize) } } @@ -487,22 +525,33 @@ pub(crate) struct OperatorValidator { } impl OperatorValidator { - pub fn new( - func_type: &FuncType, + pub fn new( + func_type: &F, locals: &[(u32, Type)], config: OperatorValidatorConfig, - ) -> OperatorValidator { - let mut local_types = Vec::new(); - local_types.extend_from_slice(&*func_type.params); - for local in locals { - for _ in 0..local.0 { - local_types.push(local.1); + ) -> OperatorValidator + where + F: WasmFuncType, + T: WasmType, + { + let local_types = { + let mut local_types = Vec::new(); + let mut n = 0; + while let Some(ty) = func_type.input_at(n) { + local_types.push(ty.to_parser_type()); + n += 1; + } + for local in locals { + for _ in 0..local.0 { + local_types.push(local.1); + } } - } - + local_types + }; let mut blocks = Vec::new(); - let mut last_returns = Vec::new(); - last_returns.extend_from_slice(&*func_type.returns); + let last_returns = wasm_func_type_outputs(func_type) + .map(WasmType::to_parser_type) + .collect::>(); blocks.push(BlockState { start_types: vec![], return_types: last_returns, @@ -555,13 +604,35 @@ impl OperatorValidator { Ok(()) } - fn check_operands(&self, expected_types: &[Type]) -> OperatorValidatorResult<()> { + fn check_operands_3( + &self, + operand1: Type, + operand2: Type, + operand3: Type, + ) -> OperatorValidatorResult<()> { + self.check_frame_size(3)?; + if !self.func_state.assert_stack_type_at(2, operand1) { + return Err("stack operand type mismatch"); + } + if !self.func_state.assert_stack_type_at(1, operand2) { + return Err("stack operand type mismatch"); + } + if !self.func_state.assert_stack_type_at(0, operand3) { + return Err("stack operand type mismatch"); + } + Ok(()) + } + + fn check_operands(&self, expected_types: I) -> OperatorValidatorResult<()> + where + I: ExactSizeIterator, + { let len = expected_types.len(); self.check_frame_size(len)?; - for (i, expected_type) in expected_types.iter().enumerate() { + for (i, expected_type) in expected_types.enumerate() { if !self .func_state - .assert_stack_type_at(len - 1 - i, *expected_type) + .assert_stack_type_at(len - 1 - i, expected_type) { return Err("stack operand type mismatch"); } @@ -802,18 +873,16 @@ impl OperatorValidator { } TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(), TypeOrFuncType::FuncType(idx) => { - let idx = idx as usize; - let types = resources.types(); - if idx >= types.len() { + if idx as usize >= resources.len_types() { return Err("type index out of bounds"); } - let ty = &types[idx]; + let ty = resources.type_at(idx); if !self.config.enable_multi_value { - if ty.returns.len() > 1 { + if ty.len_outputs() > 1 { return Err("blocks, loops, and ifs may only return at most one \ value when multi-value is not enabled"); } - if ty.params.len() > 0 { + if ty.len_inputs() > 0 { return Err("blocks, loops, and ifs accept no parameters \ when multi-value is not enabled"); } @@ -841,14 +910,14 @@ impl OperatorValidator { skip: usize, ) -> OperatorValidatorResult<()> { if let TypeOrFuncType::FuncType(idx) = ty { - let func_ty = &resources.types()[idx as usize]; - let len = func_ty.params.len(); + let func_ty = &resources.type_at(idx); + let len = func_ty.len_inputs(); self.check_frame_size(len + skip)?; for i in 0..len { - if !self - .func_state - .assert_stack_type_at(len - 1 - i + skip, func_ty.params[i]) - { + if !self.func_state.assert_stack_type_at( + len - 1 - i + skip, + func_ty.input_at(i as u32).unwrap().to_parser_type(), + ) { return Err("stack operand type mismatch for block"); } } @@ -986,25 +1055,32 @@ impl OperatorValidator { return Err("function index out of bounds"); } let type_index = resources.func_type_id_at(function_index); - let ty = &resources.types()[type_index as usize]; - self.check_operands(&ty.params)?; - self.func_state - .change_frame_with_types(ty.params.len(), &ty.returns)?; + let ty = resources.type_at(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(), + wasm_func_type_outputs(ty).map(WasmType::to_parser_type), + )?; } Operator::CallIndirect { index, table_index } => { if table_index as usize >= resources.len_tables() { return Err("table index out of bounds"); } - if index as usize >= resources.types().len() { + if index as usize >= resources.len_types() { return Err("type index out of bounds"); } - let ty = &resources.types()[index as usize]; - let mut types = Vec::with_capacity(ty.params.len() + 1); - types.extend_from_slice(&ty.params); - types.push(Type::I32); - self.check_operands(&types)?; - self.func_state - .change_frame_with_types(ty.params.len() + 1, &ty.returns)?; + let ty = resources.type_at(index); + let types = { + let mut types = Vec::with_capacity(ty.len_inputs() + 1); + types.extend(wasm_func_type_inputs(ty).map(WasmType::to_parser_type)); + types.push(Type::I32); + types + }; + self.check_operands(types.into_iter())?; + self.func_state.change_frame_with_types( + ty.len_inputs() + 1, + wasm_func_type_outputs(ty).map(WasmType::to_parser_type), + )?; } Operator::Drop => { self.check_frame_size(1)?; @@ -1015,7 +1091,7 @@ impl OperatorValidator { self.func_state.change_frame_after_select(ty)?; } Operator::TypedSelect { ty } => { - self.check_operands(&[Type::I32, ty, ty])?; + self.check_operands_3(Type::I32, ty, ty)?; self.func_state.change_frame_after_select(Some(ty))?; } Operator::LocalGet { local_index } => { @@ -1046,7 +1122,8 @@ impl OperatorValidator { return Err("global index out of bounds"); } let ty = &resources.global_at(global_index); - self.func_state.change_frame_with_type(0, ty.content_type().to_parser_type())?; + self.func_state + .change_frame_with_type(0, ty.content_type().to_parser_type())?; } Operator::GlobalSet { global_index } => { if global_index as usize >= resources.len_globals() { @@ -1533,7 +1610,7 @@ impl OperatorValidator { | Operator::I32AtomicRmw8CmpxchgU { ref memarg } => { self.check_threads_enabled()?; self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands(&[Type::I32, Type::I32, Type::I32])?; + self.check_operands_3(Type::I32, Type::I32, Type::I32)?; self.func_state.change_frame_with_type(3, Type::I32)?; } Operator::I64AtomicRmwXchg { ref memarg } @@ -1551,7 +1628,7 @@ impl OperatorValidator { | Operator::I64AtomicRmw8CmpxchgU { ref memarg } => { self.check_threads_enabled()?; self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands(&[Type::I32, Type::I64, Type::I64])?; + self.check_operands_3(Type::I32, Type::I64, Type::I64)?; self.func_state.change_frame_with_type(3, Type::I64)?; } Operator::AtomicNotify { ref memarg } => { @@ -1563,13 +1640,13 @@ impl OperatorValidator { Operator::I32AtomicWait { ref memarg } => { self.check_threads_enabled()?; self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands(&[Type::I32, Type::I32, Type::I64])?; + self.check_operands_3(Type::I32, Type::I32, Type::I64)?; self.func_state.change_frame_with_type(3, Type::I32)?; } Operator::I64AtomicWait { ref memarg } => { self.check_threads_enabled()?; self.check_shared_memarg_wo_align(memarg, resources)?; - self.check_operands(&[Type::I32, Type::I64, Type::I64])?; + self.check_operands_3(Type::I32, Type::I64, Type::I64)?; self.func_state.change_frame_with_type(3, Type::I32)?; } Operator::AtomicFence { ref flags } => { @@ -1584,7 +1661,7 @@ impl OperatorValidator { } Operator::RefIsNull => { self.check_reference_types_enabled()?; - self.check_operands(&[Type::AnyRef])?; + self.check_operands_1(Type::AnyRef)?; self.func_state.change_frame_with_type(1, Type::I32)?; } Operator::RefFunc { function_index } => { @@ -1839,7 +1916,7 @@ impl OperatorValidator { } Operator::V128Bitselect => { self.check_simd_enabled()?; - self.check_operands(&[Type::V128, Type::V128, Type::V128])?; + self.check_operands_3(Type::V128, Type::V128, Type::V128)?; self.func_state.change_frame_with_type(3, Type::V128)?; } Operator::I8x16AnyTrue @@ -1920,7 +1997,7 @@ impl OperatorValidator { return Err("segment index out of bounds"); } self.check_memory_index(0, resources)?; - self.check_operands(&[Type::I32, Type::I32, Type::I32])?; + self.check_operands_3(Type::I32, Type::I32, Type::I32)?; self.func_state.change_frame(3)?; } Operator::DataDrop { segment } => { @@ -1932,7 +2009,7 @@ impl OperatorValidator { Operator::MemoryCopy | Operator::MemoryFill => { self.check_bulk_memory_enabled()?; self.check_memory_index(0, resources)?; - self.check_operands(&[Type::I32, Type::I32, Type::I32])?; + self.check_operands_3(Type::I32, Type::I32, Type::I32)?; self.func_state.change_frame(3)?; } Operator::TableInit { segment, table } => { @@ -1946,7 +2023,7 @@ impl OperatorValidator { if table as usize >= resources.len_tables() { return Err("table index out of bounds"); } - self.check_operands(&[Type::I32, Type::I32, Type::I32])?; + self.check_operands_3(Type::I32, Type::I32, Type::I32)?; self.func_state.change_frame(3)?; } Operator::ElemDrop { segment } => { @@ -1968,7 +2045,7 @@ impl OperatorValidator { { return Err("table index out of bounds"); } - self.check_operands(&[Type::I32, Type::I32, Type::I32])?; + self.check_operands_3(Type::I32, Type::I32, Type::I32)?; self.func_state.change_frame(3)?; } Operator::TableGet { table } => { @@ -1977,7 +2054,7 @@ impl OperatorValidator { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; - self.check_operands(&[Type::I32])?; + self.check_operands_1(Type::I32)?; self.func_state.change_frame_with_type(1, ty)?; } Operator::TableSet { table } => { @@ -1986,7 +2063,7 @@ impl OperatorValidator { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; - self.check_operands(&[Type::I32, ty])?; + self.check_operands_2(Type::I32, ty)?; self.func_state.change_frame(2)?; } Operator::TableGrow { table } => { @@ -1995,7 +2072,7 @@ impl OperatorValidator { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; - self.check_operands(&[ty, Type::I32])?; + self.check_operands_2(ty, Type::I32)?; self.func_state.change_frame_with_type(2, Type::I32)?; } Operator::TableSize { table } => { @@ -2011,7 +2088,7 @@ impl OperatorValidator { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; - self.check_operands(&[Type::I32, ty, Type::I32])?; + self.check_operands_3(Type::I32, ty, Type::I32)?; self.func_state.change_frame(3)?; } } diff --git a/src/validator.rs b/src/validator.rs index bb6f2616..5bf7840e 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -157,10 +157,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.func_type_indices.len() } - fn types(&self) -> &[FuncType] { - &self.types - } - fn element_count(&self) -> u32 { self.element_count } @@ -896,7 +892,7 @@ pub fn validate_function_body< } let operators_reader = function_body.get_operators_reader()?; let func_type_index = resources.func_type_id_at(func_index); - let func_type = &resources.types()[func_type_index as usize]; + let func_type = resources.type_at(func_type_index); let mut operator_validator = OperatorValidator::new(func_type, &locals, operator_config); let mut eof_found = false; let mut last_op = 0; From 03a6ca8c687a6f2f85047c6919c69d08183b5d8b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 22:37:52 +0100 Subject: [PATCH 13/35] apply rustfmt --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a63e912a..b3f077e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,9 +66,9 @@ pub use crate::validator::ValidatingOperatorParser; pub use crate::validator::ValidatingParser; pub use crate::validator::ValidatingParserConfig; +pub use crate::operators_validator::DefaultWasmModuleResources; pub use crate::operators_validator::OperatorValidatorConfig; pub use crate::operators_validator::WasmModuleResources; -pub use crate::operators_validator::DefaultWasmModuleResources; pub use crate::readers::CodeSectionReader; pub use crate::readers::CustomSectionContent; From 2648be918ab7f6a3519f7fb71c79539ec93950dc Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Sun, 12 Jan 2020 22:46:01 +0100 Subject: [PATCH 14/35] remove DefaultWasmModuleResources (was a bad idea to begin with) --- src/lib.rs | 1 - src/operators_validator.rs | 10 ---------- src/validator.rs | 17 +++++++++++------ 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b3f077e7..4d763e97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,6 @@ pub use crate::validator::ValidatingOperatorParser; pub use crate::validator::ValidatingParser; pub use crate::validator::ValidatingParserConfig; -pub use crate::operators_validator::DefaultWasmModuleResources; pub use crate::operators_validator::OperatorValidatorConfig; pub use crate::operators_validator::WasmModuleResources; diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 9eafdfc4..7512c5d5 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -410,16 +410,6 @@ pub trait WasmModuleResources { fn data_count(&self) -> u32; } -pub trait DefaultWasmModuleResources: - WasmModuleResources< - FuncType = crate::FuncType, - TableType = crate::TableType, - MemoryType = crate::MemoryType, - GlobalType = crate::GlobalType, -> -{ -} - impl WasmType for crate::Type { fn to_parser_type(&self) -> crate::Type { *self diff --git a/src/validator.rs b/src/validator.rs index 5bf7840e..7f6e395b 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -32,9 +32,9 @@ use crate::primitives::{ use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder}; use crate::operators_validator::{ - is_subtype_supertype, DefaultWasmModuleResources, FunctionEnd, OperatorValidator, - OperatorValidatorConfig, WasmFuncType, WasmGlobalType, WasmMemoryType, WasmModuleResources, - WasmTableType, DEFAULT_OPERATOR_VALIDATOR_CONFIG, + is_subtype_supertype, FunctionEnd, OperatorValidator, OperatorValidatorConfig, WasmFuncType, + WasmGlobalType, WasmMemoryType, WasmModuleResources, WasmTableType, + DEFAULT_OPERATOR_VALIDATOR_CONFIG, }; use crate::{ElemSectionEntryTable, ElementItem}; @@ -105,8 +105,6 @@ struct ValidatingParserResources { func_type_indices: Vec, } -impl DefaultWasmModuleResources for ValidatingParserResources {} - impl<'a> WasmModuleResources for ValidatingParserResources { type FuncType = crate::FuncType; type TableType = crate::TableType; @@ -207,7 +205,14 @@ impl<'a> ValidatingParser<'a> { } } - pub fn get_resources(&self) -> &dyn DefaultWasmModuleResources { + pub fn get_resources( + &self, + ) -> &dyn WasmModuleResources< + FuncType = crate::FuncType, + TableType = crate::TableType, + MemoryType = crate::MemoryType, + GlobalType = crate::GlobalType, + > { &self.resources } From a0566fbde7a5c27c47ebb1546644e6bfad39e63f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 15 Jan 2020 22:30:12 +0100 Subject: [PATCH 15/35] remove dynamic allocations for wasm_func_type_{inputs, outputs} --- src/operators_validator.rs | 190 ++++++++++++++++++++++++++++++++----- 1 file changed, 168 insertions(+), 22 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 7512c5d5..bd278f0c 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -300,40 +300,186 @@ pub trait WasmFuncType { fn output_at(&self, at: u32) -> Option<&Self::Type>; } +/// Iterator over the inputs of a Wasm function type. +struct WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + /// The iterated-over function type. + func_type: &'a F, + /// The current starting index. + start: u32, + /// The current ending index. + end: u32, +} + +impl<'a, F, T> WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn new(func_type: &'a F) -> Self { + Self { + func_type, + start: 0, + end: func_type.len_inputs() as u32, + } + } +} + +impl<'a, F, T> Iterator for WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.start == self.end { + return None; + } + let ty = self + .func_type + .input_at(self.start) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.start += 1; + Some(ty) + } + + fn size_hint(&self) -> (usize, Option) { + (self.len(), Some(self.len())) + } +} + +impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn next_back(&mut self) -> Option { + if self.start == self.end { + return None + } + let ty = self + .func_type + .input_at(self.end) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.end -= 1; + Some(ty) + } +} + +impl<'a, F, T> ExactSizeIterator for WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn len(&self) -> usize { + (self.end as usize) - (self.start as usize) + } +} + +/// Iterator over the outputs of a Wasm function type. +struct WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + /// The iterated-over function type. + func_type: &'a F, + /// The current starting index. + start: u32, + /// The current ending index. + end: u32, +} + +impl<'a, F, T> WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn new(func_type: &'a F) -> Self { + Self { + func_type, + start: 0, + end: func_type.len_outputs() as u32, + } + } +} + +impl<'a, F, T> Iterator for WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.start == self.end { + return None; + } + let ty = self + .func_type + .output_at(self.start) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.start += 1; + Some(ty) + } + + fn size_hint(&self) -> (usize, Option) { + (self.len(), Some(self.len())) + } +} + +impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn next_back(&mut self) -> Option { + if self.start == self.end { + return None + } + let ty = self + .func_type + .output_at(self.end) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.end -= 1; + Some(ty) + } +} + +impl<'a, F, T> ExactSizeIterator for WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn len(&self) -> usize { + (self.end as usize) - (self.start as usize) + } +} + /// Returns an iterator over the input types of a Wasm function type. -fn wasm_func_type_inputs<'a, F, T>(func_type: &'a F) -> impl ExactSizeIterator +fn wasm_func_type_inputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeInputs<'a, F, T> where F: WasmFuncType, T: WasmType + 'a, { - // Quick'n dirty implementation. - // We might create an actual custom iterator - // type if performance issues arise. - let mut result = Vec::new(); - let mut n = 0; - while let Some(ty) = func_type.input_at(n) { - result.push(ty); - n += 1; - } - result.into_iter() + WasmFuncTypeInputs::new(func_type) } /// Returns an iterator over the output types of a Wasm function type. -fn wasm_func_type_outputs<'a, F, T>(func_type: &'a F) -> impl ExactSizeIterator +fn wasm_func_type_outputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeOutputs<'a, F, T> where F: WasmFuncType, T: WasmType + 'a, { - // Quick'n dirty implementation. - // We might create an actual custom iterator - // type if performance issues arise. - let mut result = Vec::new(); - let mut n = 0; - while let Some(ty) = func_type.output_at(n) { - result.push(ty); - n += 1; - } - result.into_iter() + WasmFuncTypeOutputs::new(func_type) } /// Types that qualify as Wasm table types for validation purposes. From c6bd9b7edc18a59505b8a8cfd9956bb8625ae0be Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Wed, 15 Jan 2020 22:40:03 +0100 Subject: [PATCH 16/35] apply rustfmt --- src/operators_validator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index bd278f0c..0e20ac6b 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -360,7 +360,7 @@ where { fn next_back(&mut self) -> Option { if self.start == self.end { - return None + return None; } let ty = self .func_type @@ -442,7 +442,7 @@ where { fn next_back(&mut self) -> Option { if self.start == self.end { - return None + return None; } let ty = self .func_type From 43f65e8ebb1fb61d891f43a72279e26dcd526bf8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 00:16:32 +0100 Subject: [PATCH 17/35] make use of wasm_func_type_inputs where possible instead of raw looping --- src/operators_validator.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 0e20ac6b..742cc8f9 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -671,12 +671,9 @@ impl OperatorValidator { T: WasmType, { let local_types = { - let mut local_types = Vec::new(); - let mut n = 0; - while let Some(ty) = func_type.input_at(n) { - local_types.push(ty.to_parser_type()); - n += 1; - } + let mut local_types = wasm_func_type_inputs(func_type) + .map(WasmType::to_parser_type) + .collect::>(); for local in locals { for _ in 0..local.0 { local_types.push(local.1); @@ -1046,14 +1043,11 @@ impl OperatorValidator { skip: usize, ) -> OperatorValidatorResult<()> { if let TypeOrFuncType::FuncType(idx) = ty { - let func_ty = &resources.type_at(idx); + let func_ty = resources.type_at(idx); let len = func_ty.len_inputs(); self.check_frame_size(len + skip)?; - for i in 0..len { - if !self.func_state.assert_stack_type_at( - len - 1 - i + skip, - func_ty.input_at(i as u32).unwrap().to_parser_type(), - ) { + for (i, ty) in wasm_func_type_inputs(func_ty).enumerate() { + if !self.func_state.assert_stack_type_at(len - 1 - i + skip, ty.to_parser_type()) { return Err("stack operand type mismatch for block"); } } From f4cf920d5464e9d29c7f48b12bd544013c353817 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 00:16:49 +0100 Subject: [PATCH 18/35] apply rustfmt --- src/operators_validator.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 742cc8f9..205ff7ae 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -1047,7 +1047,10 @@ impl OperatorValidator { let len = func_ty.len_inputs(); self.check_frame_size(len + skip)?; for (i, ty) in wasm_func_type_inputs(func_ty).enumerate() { - if !self.func_state.assert_stack_type_at(len - 1 - i + skip, ty.to_parser_type()) { + if !self + .func_state + .assert_stack_type_at(len - 1 - i + skip, ty.to_parser_type()) + { return Err("stack operand type mismatch for block"); } } From c438c26b25638c8df0cd8a0b0dcfec1916b7486b Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 01:15:54 +0100 Subject: [PATCH 19/35] remove WasmModuleResources::table_at and rename table_at_checked --- src/operators_validator.rs | 12 +++++------- src/validator.rs | 6 +----- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 205ff7ae..711ac4cb 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -528,10 +528,8 @@ pub trait WasmModuleResources { /// Returns the type at given index. fn type_at(&self, at: u32) -> &Self::FuncType; - /// Returns the table at given index. - fn table_at(&self, at: u32) -> &Self::TableType; /// Returns the table at given index if any. - fn table_at_checked(&self, at: u32) -> Option<&Self::TableType>; + fn table_at(&self, at: u32) -> Option<&Self::TableType>; /// Returns the linear memory at given index. fn memory_at(&self, at: u32) -> &Self::MemoryType; /// Returns the global variable at given index. @@ -2183,7 +2181,7 @@ impl OperatorValidator { } Operator::TableGet { table } => { self.check_reference_types_enabled()?; - let ty = match resources.table_at_checked(table) { + let ty = match resources.table_at(table) { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; @@ -2192,7 +2190,7 @@ impl OperatorValidator { } Operator::TableSet { table } => { self.check_reference_types_enabled()?; - let ty = match resources.table_at_checked(table) { + let ty = match resources.table_at(table) { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; @@ -2201,7 +2199,7 @@ impl OperatorValidator { } Operator::TableGrow { table } => { self.check_reference_types_enabled()?; - let ty = match resources.table_at_checked(table) { + let ty = match resources.table_at(table) { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; @@ -2217,7 +2215,7 @@ impl OperatorValidator { } Operator::TableFill { table } => { self.check_bulk_memory_enabled()?; - let ty = match resources.table_at_checked(table) { + let ty = match resources.table_at(table) { Some(ty) => ty.element_type().to_parser_type(), None => return Err("table index out of bounds"), }; diff --git a/src/validator.rs b/src/validator.rs index 7f6e395b..0726b6db 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -115,11 +115,7 @@ impl<'a> WasmModuleResources for ValidatingParserResources { &self.types[at as usize] } - fn table_at(&self, at: u32) -> &Self::TableType { - &self.tables[at as usize] - } - - fn table_at_checked(&self, at: u32) -> Option<&Self::TableType> { + fn table_at(&self, at: u32) -> Option<&Self::TableType> { self.tables.get(at as usize) } From 6b9099348e2257383ec5668322c69ab7c0e0eb66 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 01:25:53 +0100 Subject: [PATCH 20/35] make WasmModuleResources::global_at return Option<_> --- src/operators_validator.rs | 28 ++++++++++++++-------------- src/validator.rs | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 711ac4cb..b7f1f13b 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -533,7 +533,7 @@ pub trait WasmModuleResources { /// Returns the linear memory at given index. fn memory_at(&self, at: u32) -> &Self::MemoryType; /// Returns the global variable at given index. - fn global_at(&self, at: u32) -> &Self::GlobalType; + 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) -> u32; @@ -1249,23 +1249,23 @@ impl OperatorValidator { self.func_state.change_frame_with_type(1, local_type)?; } Operator::GlobalGet { global_index } => { - if global_index as usize >= resources.len_globals() { + if let Some(ty) = resources.global_at(global_index) { + self.func_state + .change_frame_with_type(0, ty.content_type().to_parser_type())?; + } else { return Err("global index out of bounds"); - } - let ty = &resources.global_at(global_index); - self.func_state - .change_frame_with_type(0, ty.content_type().to_parser_type())?; + }; } Operator::GlobalSet { global_index } => { - if global_index as usize >= resources.len_globals() { + if let Some(ty) = resources.global_at(global_index) { + if !ty.is_mutable() { + return Err("global expected to be mutable"); + } + self.check_operands_1(ty.content_type().to_parser_type())?; + self.func_state.change_frame(1)?; + } else { return Err("global index out of bounds"); - } - let ty = &resources.global_at(global_index); - if !ty.is_mutable() { - return Err("global expected to be mutable"); - } - self.check_operands_1(ty.content_type().to_parser_type())?; - self.func_state.change_frame(1)?; + }; } Operator::I32Load { ref memarg } => { self.check_memarg(memarg, 2, resources)?; diff --git a/src/validator.rs b/src/validator.rs index 0726b6db..d284fa7f 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -123,8 +123,8 @@ impl<'a> WasmModuleResources for ValidatingParserResources { &self.memories[at as usize] } - fn global_at(&self, at: u32) -> &Self::GlobalType { - &self.globals[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) -> u32 { From 05e242c2b93e03a40c270eb91f27f93a7b803a5c Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:05:28 +0100 Subject: [PATCH 21/35] remove WasmModuleResources::len_globals --- src/operators_validator.rs | 2 -- src/validator.rs | 4 ---- 2 files changed, 6 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index b7f1f13b..5ab44fea 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -543,8 +543,6 @@ pub trait WasmModuleResources { fn len_tables(&self) -> usize; /// Returns the number of linear memories. fn len_memories(&self) -> usize; - /// Returns the number of global variables. - fn len_globals(&self) -> usize; /// Returns the number of function type indices. fn len_func_type_id(&self) -> usize; diff --git a/src/validator.rs b/src/validator.rs index d284fa7f..3aa213ab 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -143,10 +143,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.memories.len() } - fn len_globals(&self) -> usize { - self.globals.len() - } - fn len_func_type_id(&self) -> usize { self.func_type_indices.len() } From 31f3b6586aca30264df912d73f3c0001433899f6 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:05:47 +0100 Subject: [PATCH 22/35] remove WasmModuleResources::len_tables --- src/operators_validator.rs | 12 +++++------- src/validator.rs | 4 ---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 5ab44fea..b5b8b8c3 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -539,8 +539,6 @@ pub trait WasmModuleResources { /// Returns the number of types. fn len_types(&self) -> usize; - /// Returns the number of tables. - fn len_tables(&self) -> usize; /// Returns the number of linear memories. fn len_memories(&self) -> usize; /// Returns the number of function type indices. @@ -1192,7 +1190,7 @@ impl OperatorValidator { )?; } Operator::CallIndirect { index, table_index } => { - if table_index as usize >= resources.len_tables() { + if resources.table_at(table_index).is_none() { return Err("table index out of bounds"); } if index as usize >= resources.len_types() { @@ -2149,7 +2147,7 @@ impl OperatorValidator { if table > 0 { self.check_reference_types_enabled()?; } - if table as usize >= resources.len_tables() { + if resources.table_at(table).is_none() { return Err("table index out of bounds"); } self.check_operands_3(Type::I32, Type::I32, Type::I32)?; @@ -2169,8 +2167,8 @@ impl OperatorValidator { if src_table > 0 || dst_table > 0 { self.check_reference_types_enabled()?; } - if src_table as usize >= resources.len_tables() - || dst_table as usize >= resources.len_tables() + if resources.table_at(src_table).is_none() + || resources.table_at(dst_table).is_none() { return Err("table index out of bounds"); } @@ -2206,7 +2204,7 @@ impl OperatorValidator { } Operator::TableSize { table } => { self.check_reference_types_enabled()?; - if table as usize >= resources.len_tables() { + if resources.table_at(table).is_none() { return Err("table index out of bounds"); } self.func_state.change_frame_with_type(0, Type::I32)?; diff --git a/src/validator.rs b/src/validator.rs index 3aa213ab..c3da2bcc 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -135,10 +135,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.types.len() } - fn len_tables(&self) -> usize { - self.tables.len() - } - fn len_memories(&self) -> usize { self.memories.len() } From 00bcfc7b9c695d703f0f5149a62ad8888a901bb3 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:09:55 +0100 Subject: [PATCH 23/35] make WasmModuleResources::memory_at return an Option --- src/operators_validator.rs | 14 +++++++------- src/validator.rs | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index b5b8b8c3..6d0d2e57 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -531,7 +531,7 @@ pub trait WasmModuleResources { /// Returns the table at given index if any. fn table_at(&self, at: u32) -> Option<&Self::TableType>; /// Returns the linear memory at given index. - fn memory_at(&self, at: u32) -> &Self::MemoryType; + fn memory_at(&self, at: u32) -> Option<&Self::MemoryType>; /// Returns the global variable at given index. fn global_at(&self, at: u32) -> Option<&Self::GlobalType>; /// Returns the function signature ID at given index. @@ -883,13 +883,13 @@ impl OperatorValidator { GlobalType = G, >, ) -> OperatorValidatorResult<()> { - if memory_index as usize >= resources.len_memories() { - return Err("no linear memories are present"); - } - if !resources.memory_at(memory_index).is_shared() { - return Err("atomic accesses require shared memory"); + match resources.memory_at(memory_index) { + Some(memory) if !memory.is_shared() => { + return Err("atomic accesses require shared memory") + } + None => return Err("no linear memories are present"), + _ => Ok(()) } - Ok(()) } fn check_memarg( diff --git a/src/validator.rs b/src/validator.rs index c3da2bcc..7f4a9820 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -119,8 +119,8 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.tables.get(at as usize) } - fn memory_at(&self, at: u32) -> &Self::MemoryType { - &self.memories[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> { From 94e208cfbed2f34b60078b1754b3f9b63b3b98f7 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:11:08 +0100 Subject: [PATCH 24/35] remove WasmModuleResources::len_memories --- src/operators_validator.rs | 4 +--- src/validator.rs | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 6d0d2e57..2931d8e4 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -539,8 +539,6 @@ pub trait WasmModuleResources { /// Returns the number of types. fn len_types(&self) -> usize; - /// Returns the number of linear memories. - fn len_memories(&self) -> usize; /// Returns the number of function type indices. fn len_func_type_id(&self) -> usize; @@ -862,7 +860,7 @@ impl OperatorValidator { GlobalType = G, >, ) -> OperatorValidatorResult<()> { - if memory_index as usize >= resources.len_memories() { + if resources.memory_at(memory_index).is_none() { return Err("no linear memories are present"); } Ok(()) diff --git a/src/validator.rs b/src/validator.rs index 7f4a9820..54215bfe 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -135,10 +135,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.types.len() } - fn len_memories(&self) -> usize { - self.memories.len() - } - fn len_func_type_id(&self) -> usize { self.func_type_indices.len() } From 0aac6b8444f45cf686dad1a470fb0cfc2ab67663 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:20:47 +0100 Subject: [PATCH 25/35] make WasmModuleResources::func_type_id_at return an Option --- src/operators_validator.rs | 21 +++++++++++---------- src/validator.rs | 11 ++++++++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 2931d8e4..0e01f518 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -535,7 +535,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) -> u32; + fn func_type_id_at(&self, at: u32) -> Option; /// Returns the number of types. fn len_types(&self) -> usize; @@ -1176,16 +1176,17 @@ impl OperatorValidator { self.func_state.start_dead_code() } Operator::Call { function_index } => { - if function_index as usize >= resources.len_func_type_id() { - return Err("function index out of bounds"); + match resources.func_type_id_at(function_index) { + Some(type_index) => { + let ty = resources.type_at(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(), + wasm_func_type_outputs(ty).map(WasmType::to_parser_type), + )?; + } + None => return Err("function index out of bounds"), } - let type_index = resources.func_type_id_at(function_index); - let ty = resources.type_at(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(), - wasm_func_type_outputs(ty).map(WasmType::to_parser_type), - )?; } Operator::CallIndirect { index, table_index } => { if resources.table_at(table_index).is_none() { diff --git a/src/validator.rs b/src/validator.rs index 54215bfe..3a258085 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -127,8 +127,8 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.globals.get(at as usize) } - fn func_type_id_at(&self, at: u32) -> u32 { - self.func_type_indices[at as usize] + fn func_type_id_at(&self, at: u32) -> Option { + self.func_type_indices.get(at as usize).copied() } fn len_types(&self) -> usize { @@ -880,7 +880,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); + let func_type_index = resources + .func_type_id_at(func_index) + // 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 validated function id itself is out of bounds"); let func_type = resources.type_at(func_type_index); let mut operator_validator = OperatorValidator::new(func_type, &locals, operator_config); let mut eof_found = false; From dcae0a97f8d00444a8cdddafa0ed5251c4456c1f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:22:05 +0100 Subject: [PATCH 26/35] remove WasmModuleResources::len_func_type_id --- src/operators_validator.rs | 4 +--- src/validator.rs | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 0e01f518..973b1bd9 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -539,8 +539,6 @@ pub trait WasmModuleResources { /// Returns the number of types. fn len_types(&self) -> usize; - /// Returns the number of function type indices. - fn len_func_type_id(&self) -> usize; /// Returns the number of elements. fn element_count(&self) -> u32; @@ -1792,7 +1790,7 @@ impl OperatorValidator { } Operator::RefFunc { function_index } => { self.check_reference_types_enabled()?; - if function_index as usize >= resources.len_func_type_id() { + if resources.func_type_id_at(function_index).is_none() { return Err("function index out of bounds"); } self.func_state.change_frame_with_type(0, Type::AnyFunc)?; diff --git a/src/validator.rs b/src/validator.rs index 3a258085..12326023 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -135,10 +135,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.types.len() } - fn len_func_type_id(&self) -> usize { - self.func_type_indices.len() - } - fn element_count(&self) -> u32 { self.element_count } From 1f895070002c8c0699679c8321a3bec1b546da2f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:39:26 +0100 Subject: [PATCH 27/35] make WasmModuleResources::type_at return Option --- src/operators_validator.rs | 97 ++++++++++++++++++++++---------------- src/validator.rs | 15 ++++-- 2 files changed, 66 insertions(+), 46 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 973b1bd9..af5f7f72 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -134,7 +134,13 @@ impl FuncState { TypeOrFuncType::Type(Type::EmptyBlockType) => (vec![], vec![]), TypeOrFuncType::Type(ty) => (vec![], vec![ty]), TypeOrFuncType::FuncType(idx) => { - let ty = resources.type_at(idx); + let ty = resources + .type_at(idx) + // Note: This was an out-of-bounds memory access before + // the change to return `Option` at `type_at`. So + // I assumed that invalid indices at this point are + // bugs. + .expect("function type index is out of bounds"); ( wasm_func_type_inputs(ty) .map(WasmType::to_parser_type) @@ -527,7 +533,7 @@ pub trait WasmModuleResources { type GlobalType: WasmGlobalType; /// Returns the type at given index. - fn type_at(&self, at: u32) -> &Self::FuncType; + fn type_at(&self, at: u32) -> Option<&Self::FuncType>; /// Returns the table at given index if any. fn table_at(&self, at: u32) -> Option<&Self::TableType>; /// Returns the linear memory at given index. @@ -884,7 +890,7 @@ impl OperatorValidator { return Err("atomic accesses require shared memory") } None => return Err("no linear memories are present"), - _ => Ok(()) + _ => Ok(()), } } @@ -996,21 +1002,21 @@ impl OperatorValidator { } TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(), TypeOrFuncType::FuncType(idx) => { - if idx as usize >= resources.len_types() { - return Err("type index out of bounds"); - } - let ty = resources.type_at(idx); - if !self.config.enable_multi_value { - if ty.len_outputs() > 1 { - return Err("blocks, loops, and ifs may only return at most one \ + match resources.type_at(idx) { + None => Err("type index out of bounds"), + Some(ty) if !self.config.enable_multi_value => { + if ty.len_outputs() > 1 { + return Err("blocks, loops, and ifs may only return at most one \ value when multi-value is not enabled"); - } - if ty.len_inputs() > 0 { - return Err("blocks, loops, and ifs accept no parameters \ + } + if ty.len_inputs() > 0 { + return Err("blocks, loops, and ifs accept no parameters \ when multi-value is not enabled"); + } + Ok(()) } + Some(_) => Ok(()), } - Ok(()) } _ => Err("invalid block return type"), } @@ -1033,7 +1039,12 @@ impl OperatorValidator { skip: usize, ) -> OperatorValidatorResult<()> { if let TypeOrFuncType::FuncType(idx) = ty { - let func_ty = resources.type_at(idx); + let func_ty = resources.type_at(idx) + // Note: This was an out-of-bounds memory access before + // the change to return `Option` at `type_at`. So + // I assumed that invalid indices at this point are + // bugs. + .expect("function type index is out of bounds"); let len = func_ty.len_inputs(); self.check_frame_size(len + skip)?; for (i, ty) in wasm_func_type_inputs(func_ty).enumerate() { @@ -1173,38 +1184,42 @@ impl OperatorValidator { self.check_jump_from_block(depth, 0)?; self.func_state.start_dead_code() } - Operator::Call { function_index } => { - match resources.func_type_id_at(function_index) { - Some(type_index) => { - let ty = resources.type_at(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(), - wasm_func_type_outputs(ty).map(WasmType::to_parser_type), - )?; - } - None => return Err("function index out of bounds"), + Operator::Call { function_index } => match resources.func_type_id_at(function_index) { + Some(type_index) => { + let ty = resources.type_at(type_index) + // Note: This was an out-of-bounds memory access before + // the change to return `Option` at `type_at`. So + // I assumed that invalid indices at this point are + // bugs. + .expect("function type index is out of bounds"); + self.check_operands(wasm_func_type_inputs(ty).map(WasmType::to_parser_type))?; + self.func_state.change_frame_with_types( + ty.len_inputs(), + wasm_func_type_outputs(ty).map(WasmType::to_parser_type), + )?; } - } + None => return Err("function index out of bounds"), + }, Operator::CallIndirect { index, table_index } => { if resources.table_at(table_index).is_none() { return Err("table index out of bounds"); } - if index as usize >= resources.len_types() { - return Err("type index out of bounds"); + match resources.type_at(index) { + None => return Err("type index out of bounds"), + Some(ty) => { + let types = { + let mut types = Vec::with_capacity(ty.len_inputs() + 1); + types.extend(wasm_func_type_inputs(ty).map(WasmType::to_parser_type)); + types.push(Type::I32); + types + }; + self.check_operands(types.into_iter())?; + self.func_state.change_frame_with_types( + ty.len_inputs() + 1, + wasm_func_type_outputs(ty).map(WasmType::to_parser_type), + )?; + } } - let ty = resources.type_at(index); - let types = { - let mut types = Vec::with_capacity(ty.len_inputs() + 1); - types.extend(wasm_func_type_inputs(ty).map(WasmType::to_parser_type)); - types.push(Type::I32); - types - }; - self.check_operands(types.into_iter())?; - self.func_state.change_frame_with_types( - ty.len_inputs() + 1, - wasm_func_type_outputs(ty).map(WasmType::to_parser_type), - )?; } Operator::Drop => { self.check_frame_size(1)?; diff --git a/src/validator.rs b/src/validator.rs index 12326023..9cfeeb19 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -111,8 +111,8 @@ impl<'a> WasmModuleResources for ValidatingParserResources { type MemoryType = crate::MemoryType; type GlobalType = crate::GlobalType; - fn type_at(&self, at: u32) -> &Self::FuncType { - &self.types[at as usize] + fn type_at(&self, at: u32) -> Option<&Self::FuncType> { + self.types.get(at as usize) } fn table_at(&self, at: u32) -> Option<&Self::TableType> { @@ -878,11 +878,16 @@ pub fn validate_function_body< let operators_reader = function_body.get_operators_reader()?; let func_type_index = resources .func_type_id_at(func_index) - // This was an out-of-bounds access before the change to return `Option` + // 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 validated function id itself is out of bounds"); - let func_type = resources.type_at(func_type_index); + .expect("the function index of the validated function itself is out of bounds"); + let func_type = resources + .type_at(func_type_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"); let mut operator_validator = OperatorValidator::new(func_type, &locals, operator_config); let mut eof_found = false; let mut last_op = 0; From 4937b8a2be69493d5e1cd4bcc93c9ae2c92692e8 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:40:10 +0100 Subject: [PATCH 28/35] remove WasmModuleResources::len_types --- src/operators_validator.rs | 3 --- src/validator.rs | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index af5f7f72..4101358b 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -543,9 +543,6 @@ pub trait WasmModuleResources { /// Returns the function signature ID at given index. fn func_type_id_at(&self, at: u32) -> Option; - /// Returns the number of types. - fn len_types(&self) -> usize; - /// Returns the number of elements. fn element_count(&self) -> u32; /// Returns the number of bytes in the Wasm data section. diff --git a/src/validator.rs b/src/validator.rs index 9cfeeb19..e51b362b 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -131,10 +131,6 @@ impl<'a> WasmModuleResources for ValidatingParserResources { self.func_type_indices.get(at as usize).copied() } - fn len_types(&self) -> usize { - self.types.len() - } - fn element_count(&self) -> u32 { self.element_count } From 6ab86d42699e8816355b979d39e67ac3aa116ab5 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 10:52:30 +0100 Subject: [PATCH 29/35] apply rustfmt --- src/operators_validator.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 4101358b..b99bcbd2 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -998,23 +998,21 @@ impl OperatorValidator { self.check_reference_types_enabled() } TypeOrFuncType::Type(Type::V128) => self.check_simd_enabled(), - TypeOrFuncType::FuncType(idx) => { - match resources.type_at(idx) { - None => Err("type index out of bounds"), - Some(ty) if !self.config.enable_multi_value => { - if ty.len_outputs() > 1 { - return Err("blocks, loops, and ifs may only return at most one \ + TypeOrFuncType::FuncType(idx) => match resources.type_at(idx) { + None => Err("type index out of bounds"), + Some(ty) if !self.config.enable_multi_value => { + if ty.len_outputs() > 1 { + return Err("blocks, loops, and ifs may only return at most one \ value when multi-value is not enabled"); - } - if ty.len_inputs() > 0 { - return Err("blocks, loops, and ifs accept no parameters \ + } + if ty.len_inputs() > 0 { + return Err("blocks, loops, and ifs accept no parameters \ when multi-value is not enabled"); - } - Ok(()) } - Some(_) => Ok(()), + Ok(()) } - } + Some(_) => Ok(()), + }, _ => Err("invalid block return type"), } } @@ -1036,7 +1034,8 @@ impl OperatorValidator { skip: usize, ) -> OperatorValidatorResult<()> { if let TypeOrFuncType::FuncType(idx) = ty { - let func_ty = resources.type_at(idx) + let func_ty = resources + .type_at(idx) // Note: This was an out-of-bounds memory access before // the change to return `Option` at `type_at`. So // I assumed that invalid indices at this point are @@ -1183,7 +1182,8 @@ impl OperatorValidator { } Operator::Call { function_index } => match resources.func_type_id_at(function_index) { Some(type_index) => { - let ty = resources.type_at(type_index) + let ty = resources + .type_at(type_index) // Note: This was an out-of-bounds memory access before // the change to return `Option` at `type_at`. So // I assumed that invalid indices at this point are From eadd74d6e19db9369757c9e11db88065d6daa42f Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 12:23:17 +0100 Subject: [PATCH 30/35] Add docs to WasmModuleResources --- src/operators_validator.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index b99bcbd2..033cb2e0 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -522,6 +522,14 @@ pub trait WasmGlobalType { fn content_type(&self) -> &Self::Type; } +/// Types that qualify as Wasm valiation database. +/// +/// # Note +/// +/// The `wasmparser` crate provides a builtin validation framework but allows +/// users of this crate to also feed the parsed Wasm into their own data +/// structure while parsing and also validate at the same time without +/// the need of an additional parsing or validation step or copying data around. pub trait WasmModuleResources { /// The function type used for validation. type FuncType: WasmFuncType; From 30d9372f51d97c1a95d53c4774f2257a06396f89 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Thu, 16 Jan 2020 12:26:34 +0100 Subject: [PATCH 31/35] export new validation traits --- src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 4d763e97..bfa7a51f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,7 +67,12 @@ pub use crate::validator::ValidatingParser; pub use crate::validator::ValidatingParserConfig; pub use crate::operators_validator::OperatorValidatorConfig; +pub use crate::operators_validator::WasmFuncType; +pub use crate::operators_validator::WasmGlobalType; +pub use crate::operators_validator::WasmMemoryType; pub use crate::operators_validator::WasmModuleResources; +pub use crate::operators_validator::WasmTableType; +pub use crate::operators_validator::WasmType; pub use crate::readers::CodeSectionReader; pub use crate::readers::CustomSectionContent; From a10ab508ac514c7c2658c704acbdf59af529553c Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Fri, 17 Jan 2020 00:29:49 +0100 Subject: [PATCH 32/35] fix type in docs Co-Authored-By: Yury Delendik --- src/operators_validator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 033cb2e0..5b176ead 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -522,7 +522,7 @@ pub trait WasmGlobalType { fn content_type(&self) -> &Self::Type; } -/// Types that qualify as Wasm valiation database. +/// Types that qualify as Wasm valiation database. /// /// # Note /// From 0bb3c6b04969b11ff4a0ac4132777bae6466ee79 Mon Sep 17 00:00:00 2001 From: Hero Bird Date: Fri, 17 Jan 2020 00:30:08 +0100 Subject: [PATCH 33/35] fix typo in docs Co-Authored-By: Yury Delendik --- src/operators_validator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 5b176ead..f75a203e 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -268,7 +268,7 @@ enum BlockType { If, } -/// Types that quality as Wasm types for validation purposes. +/// Types that qualify as Wasm types for validation purposes. /// /// Must be comparable with `wasmparser` given Wasm types and /// must be comparable to themselves. From 7f8b39553b439fee3cf7584cfa03657ebb8e685a Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 17 Jan 2020 02:02:21 +0100 Subject: [PATCH 34/35] move new traits, iterators and functions for validation into their own module --- src/lib.rs | 19 +- src/module_resources.rs | 370 +++++++++++++++++++++++++++++++++++++ src/operators_validator.rs | 360 +----------------------------------- src/validator.rs | 10 +- 4 files changed, 393 insertions(+), 366 deletions(-) create mode 100644 src/module_resources.rs diff --git a/src/lib.rs b/src/lib.rs index bfa7a51f..8be9613e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,13 +66,19 @@ pub use crate::validator::ValidatingOperatorParser; pub use crate::validator::ValidatingParser; pub use crate::validator::ValidatingParserConfig; +pub use crate::module_resources::WasmFuncType; +pub use crate::module_resources::WasmGlobalType; +pub use crate::module_resources::WasmMemoryType; +pub use crate::module_resources::WasmModuleResources; +pub use crate::module_resources::WasmTableType; +pub use crate::module_resources::WasmType; + +pub(crate) use crate::module_resources::{ + wasm_func_type_inputs, + wasm_func_type_outputs, +}; + pub use crate::operators_validator::OperatorValidatorConfig; -pub use crate::operators_validator::WasmFuncType; -pub use crate::operators_validator::WasmGlobalType; -pub use crate::operators_validator::WasmMemoryType; -pub use crate::operators_validator::WasmModuleResources; -pub use crate::operators_validator::WasmTableType; -pub use crate::operators_validator::WasmType; pub use crate::readers::CodeSectionReader; pub use crate::readers::CustomSectionContent; @@ -118,6 +124,7 @@ pub use crate::readers::TypeSectionReader; mod binary_reader; mod limits; +mod module_resources; mod operators_validator; mod parser; mod primitives; diff --git a/src/module_resources.rs b/src/module_resources.rs new file mode 100644 index 00000000..fb2e4c0d --- /dev/null +++ b/src/module_resources.rs @@ -0,0 +1,370 @@ +/* Copyright 2019 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/// Types that quality as Wasm types for validation purposes. +/// +/// Must be comparable with `wasmparser` given Wasm types and +/// must be comparable to themselves. +pub trait WasmType: PartialEq + PartialEq + Eq { + /// Converts the custom type into a `wasmparser` known type. + /// + /// # Note + /// + /// This interface is required as bridge until transitioning is complete. + fn to_parser_type(&self) -> crate::Type; +} + +/// Types that qualify as Wasm function types for validation purposes. +pub trait WasmFuncType { + /// A type that is comparable with Wasm types. + type Type: WasmType; + + /// Returns the number of input types. + fn len_inputs(&self) -> usize; + /// Returns the number of output types. + fn len_outputs(&self) -> usize; + /// Returns the type at given index if any. + /// + /// # Note + /// + /// The returned type may be wrapped by the user crate and thus + /// the actually returned type only has to be comparable to a Wasm type. + fn input_at(&self, at: u32) -> Option<&Self::Type>; + /// Returns the type at given index if any. + /// + /// # Note + /// + /// The returned type may be wrapped by the user crate and thus + /// the actually returned type only has to be comparable to a Wasm type. + fn output_at(&self, at: u32) -> Option<&Self::Type>; +} + +/// Iterator over the inputs of a Wasm function type. +pub(crate) struct WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + /// The iterated-over function type. + func_type: &'a F, + /// The current starting index. + start: u32, + /// The current ending index. + end: u32, +} + +impl<'a, F, T> WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn new(func_type: &'a F) -> Self { + Self { + func_type, + start: 0, + end: func_type.len_inputs() as u32, + } + } +} + +impl<'a, F, T> Iterator for WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.start == self.end { + return None; + } + let ty = self + .func_type + .input_at(self.start) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.start += 1; + Some(ty) + } + + fn size_hint(&self) -> (usize, Option) { + (self.len(), Some(self.len())) + } +} + +impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn next_back(&mut self) -> Option { + if self.start == self.end { + return None; + } + let ty = self + .func_type + .input_at(self.end) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.end -= 1; + Some(ty) + } +} + +impl<'a, F, T> ExactSizeIterator for WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn len(&self) -> usize { + (self.end as usize) - (self.start as usize) + } +} + +/// Iterator over the outputs of a Wasm function type. +pub(crate) struct WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + /// The iterated-over function type. + func_type: &'a F, + /// The current starting index. + start: u32, + /// The current ending index. + end: u32, +} + +impl<'a, F, T> WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn new(func_type: &'a F) -> Self { + Self { + func_type, + start: 0, + end: func_type.len_outputs() as u32, + } + } +} + +impl<'a, F, T> Iterator for WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.start == self.end { + return None; + } + let ty = self + .func_type + .output_at(self.start) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.start += 1; + Some(ty) + } + + fn size_hint(&self) -> (usize, Option) { + (self.len(), Some(self.len())) + } +} + +impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn next_back(&mut self) -> Option { + if self.start == self.end { + return None; + } + let ty = self + .func_type + .output_at(self.end) + // Expected since `self.start != self.end`. + .expect("we expect to receive an input type at this point"); + self.end -= 1; + Some(ty) + } +} + +impl<'a, F, T> ExactSizeIterator for WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + fn len(&self) -> usize { + (self.end as usize) - (self.start as usize) + } +} + +/// Returns an iterator over the input types of a Wasm function type. +pub(crate) fn wasm_func_type_inputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeInputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + WasmFuncTypeInputs::new(func_type) +} + +/// Returns an iterator over the output types of a Wasm function type. +pub(crate) fn wasm_func_type_outputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeOutputs<'a, F, T> +where + F: WasmFuncType, + T: WasmType + 'a, +{ + WasmFuncTypeOutputs::new(func_type) +} + +/// Types that qualify as Wasm table types for validation purposes. +pub trait WasmTableType { + /// A type that is comparable with Wasm types. + type Type: WasmType; + + /// Returns the element type of the table. + fn element_type(&self) -> &Self::Type; + /// Returns the initial limit of the table. + fn initial_limit(&self) -> u32; + /// Returns the maximum limit of the table if any. + fn maximum_limit(&self) -> Option; +} + +/// Types that qualify as Wasm memory types for validation purposes. +pub trait WasmMemoryType { + /// Returns `true` if the linear memory is shared. + fn is_shared(&self) -> bool; + /// Returns the initial limit of the linear memory. + fn initial_limit(&self) -> u32; + /// Returns the maximum limit of the linear memory if any. + fn maximum_limit(&self) -> Option; +} + +/// Types that qualify as Wasm global types for validation purposes. +pub trait WasmGlobalType { + /// A type that is comparable with Wasm types. + type Type: WasmType; + + /// Returns `true` if the global variable is mutable. + fn is_mutable(&self) -> bool; + /// Returns the content type of the global variable. + fn content_type(&self) -> &Self::Type; +} + +/// Types that qualify as Wasm valiation database. +/// +/// # Note +/// +/// The `wasmparser` crate provides a builtin validation framework but allows +/// users of this crate to also feed the parsed Wasm into their own data +/// structure while parsing and also validate at the same time without +/// the need of an additional parsing or validation step or copying data around. +pub trait WasmModuleResources { + /// The function type used for validation. + type FuncType: WasmFuncType; + /// The table type used for validation. + type TableType: WasmTableType; + /// The memory type used for validation. + type MemoryType: WasmMemoryType; + /// The global type used for validation. + type GlobalType: WasmGlobalType; + + /// Returns the type at given index. + fn type_at(&self, at: u32) -> Option<&Self::FuncType>; + /// Returns the table at given index if any. + fn table_at(&self, at: u32) -> Option<&Self::TableType>; + /// Returns the linear memory at given index. + fn memory_at(&self, at: u32) -> Option<&Self::MemoryType>; + /// 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; + + /// Returns the number of elements. + fn element_count(&self) -> u32; + /// Returns the number of bytes in the Wasm data section. + fn data_count(&self) -> u32; +} + +impl WasmType for crate::Type { + fn to_parser_type(&self) -> crate::Type { + *self + } +} + +impl WasmFuncType for crate::FuncType { + type Type = crate::Type; + + fn len_inputs(&self) -> usize { + self.params.len() + } + + fn len_outputs(&self) -> usize { + self.returns.len() + } + + fn input_at(&self, at: u32) -> Option<&Self::Type> { + self.params.get(at as usize) + } + + fn output_at(&self, at: u32) -> Option<&Self::Type> { + self.returns.get(at as usize) + } +} + +impl WasmGlobalType for crate::GlobalType { + type Type = crate::Type; + + fn is_mutable(&self) -> bool { + self.mutable + } + + fn content_type(&self) -> &Self::Type { + &self.content_type + } +} + +impl WasmTableType for crate::TableType { + type Type = crate::Type; + + fn element_type(&self) -> &Self::Type { + &self.element_type + } + + fn initial_limit(&self) -> u32 { + self.limits.initial + } + + fn maximum_limit(&self) -> Option { + self.limits.maximum + } +} + +impl WasmMemoryType for crate::MemoryType { + fn is_shared(&self) -> bool { + self.shared + } + + fn initial_limit(&self) -> u32 { + self.limits.initial + } + fn maximum_limit(&self) -> Option { + self.limits.maximum + } +} diff --git a/src/operators_validator.rs b/src/operators_validator.rs index 033cb2e0..b602942b 100644 --- a/src/operators_validator.rs +++ b/src/operators_validator.rs @@ -18,6 +18,10 @@ use std::result; use std::str; use crate::primitives::{MemoryImmediate, Operator, SIMDLaneIndex, Type, TypeOrFuncType}; +use crate::{ + wasm_func_type_inputs, wasm_func_type_outputs, WasmFuncType, WasmGlobalType, WasmMemoryType, + WasmModuleResources, WasmTableType, WasmType, +}; /// Test if `subtype` is a subtype of `supertype`. pub(crate) fn is_subtype_supertype(subtype: Type, supertype: Type) -> bool { @@ -268,362 +272,6 @@ enum BlockType { If, } -/// Types that quality as Wasm types for validation purposes. -/// -/// Must be comparable with `wasmparser` given Wasm types and -/// must be comparable to themselves. -pub trait WasmType: PartialEq + PartialEq + Eq { - /// Converts the custom type into a `wasmparser` known type. - /// - /// # Note - /// - /// This interface is required as bridge until transitioning is complete. - fn to_parser_type(&self) -> crate::Type; -} - -/// Types that qualify as Wasm function types for validation purposes. -pub trait WasmFuncType { - /// A type that is comparable with Wasm types. - type Type: WasmType; - - /// Returns the number of input types. - fn len_inputs(&self) -> usize; - /// Returns the number of output types. - fn len_outputs(&self) -> usize; - /// Returns the type at given index if any. - /// - /// # Note - /// - /// The returned type may be wrapped by the user crate and thus - /// the actually returned type only has to be comparable to a Wasm type. - fn input_at(&self, at: u32) -> Option<&Self::Type>; - /// Returns the type at given index if any. - /// - /// # Note - /// - /// The returned type may be wrapped by the user crate and thus - /// the actually returned type only has to be comparable to a Wasm type. - fn output_at(&self, at: u32) -> Option<&Self::Type>; -} - -/// Iterator over the inputs of a Wasm function type. -struct WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - /// The iterated-over function type. - func_type: &'a F, - /// The current starting index. - start: u32, - /// The current ending index. - end: u32, -} - -impl<'a, F, T> WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - fn new(func_type: &'a F) -> Self { - Self { - func_type, - start: 0, - end: func_type.len_inputs() as u32, - } - } -} - -impl<'a, F, T> Iterator for WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .input_at(self.start) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.start += 1; - Some(ty) - } - - fn size_hint(&self) -> (usize, Option) { - (self.len(), Some(self.len())) - } -} - -impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - fn next_back(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .input_at(self.end) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.end -= 1; - Some(ty) - } -} - -impl<'a, F, T> ExactSizeIterator for WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - fn len(&self) -> usize { - (self.end as usize) - (self.start as usize) - } -} - -/// Iterator over the outputs of a Wasm function type. -struct WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - /// The iterated-over function type. - func_type: &'a F, - /// The current starting index. - start: u32, - /// The current ending index. - end: u32, -} - -impl<'a, F, T> WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - fn new(func_type: &'a F) -> Self { - Self { - func_type, - start: 0, - end: func_type.len_outputs() as u32, - } - } -} - -impl<'a, F, T> Iterator for WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .output_at(self.start) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.start += 1; - Some(ty) - } - - fn size_hint(&self) -> (usize, Option) { - (self.len(), Some(self.len())) - } -} - -impl<'a, F, T> DoubleEndedIterator for WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - fn next_back(&mut self) -> Option { - if self.start == self.end { - return None; - } - let ty = self - .func_type - .output_at(self.end) - // Expected since `self.start != self.end`. - .expect("we expect to receive an input type at this point"); - self.end -= 1; - Some(ty) - } -} - -impl<'a, F, T> ExactSizeIterator for WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - fn len(&self) -> usize { - (self.end as usize) - (self.start as usize) - } -} - -/// Returns an iterator over the input types of a Wasm function type. -fn wasm_func_type_inputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeInputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - WasmFuncTypeInputs::new(func_type) -} - -/// Returns an iterator over the output types of a Wasm function type. -fn wasm_func_type_outputs<'a, F, T>(func_type: &'a F) -> WasmFuncTypeOutputs<'a, F, T> -where - F: WasmFuncType, - T: WasmType + 'a, -{ - WasmFuncTypeOutputs::new(func_type) -} - -/// Types that qualify as Wasm table types for validation purposes. -pub trait WasmTableType { - /// A type that is comparable with Wasm types. - type Type: WasmType; - - /// Returns the element type of the table. - fn element_type(&self) -> &Self::Type; - /// Returns the initial limit of the table. - fn initial_limit(&self) -> u32; - /// Returns the maximum limit of the table if any. - fn maximum_limit(&self) -> Option; -} - -/// Types that qualify as Wasm memory types for validation purposes. -pub trait WasmMemoryType { - /// Returns `true` if the linear memory is shared. - fn is_shared(&self) -> bool; - /// Returns the initial limit of the linear memory. - fn initial_limit(&self) -> u32; - /// Returns the maximum limit of the linear memory if any. - fn maximum_limit(&self) -> Option; -} - -/// Types that qualify as Wasm global types for validation purposes. -pub trait WasmGlobalType { - /// A type that is comparable with Wasm types. - type Type: WasmType; - - /// Returns `true` if the global variable is mutable. - fn is_mutable(&self) -> bool; - /// Returns the content type of the global variable. - fn content_type(&self) -> &Self::Type; -} - -/// Types that qualify as Wasm valiation database. -/// -/// # Note -/// -/// The `wasmparser` crate provides a builtin validation framework but allows -/// users of this crate to also feed the parsed Wasm into their own data -/// structure while parsing and also validate at the same time without -/// the need of an additional parsing or validation step or copying data around. -pub trait WasmModuleResources { - /// The function type used for validation. - type FuncType: WasmFuncType; - /// The table type used for validation. - type TableType: WasmTableType; - /// The memory type used for validation. - type MemoryType: WasmMemoryType; - /// The global type used for validation. - type GlobalType: WasmGlobalType; - - /// Returns the type at given index. - fn type_at(&self, at: u32) -> Option<&Self::FuncType>; - /// Returns the table at given index if any. - fn table_at(&self, at: u32) -> Option<&Self::TableType>; - /// Returns the linear memory at given index. - fn memory_at(&self, at: u32) -> Option<&Self::MemoryType>; - /// 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; - - /// Returns the number of elements. - fn element_count(&self) -> u32; - /// Returns the number of bytes in the Wasm data section. - fn data_count(&self) -> u32; -} - -impl WasmType for crate::Type { - fn to_parser_type(&self) -> crate::Type { - *self - } -} - -impl WasmFuncType for crate::FuncType { - type Type = crate::Type; - - fn len_inputs(&self) -> usize { - self.params.len() - } - - fn len_outputs(&self) -> usize { - self.returns.len() - } - - fn input_at(&self, at: u32) -> Option<&Self::Type> { - self.params.get(at as usize) - } - - fn output_at(&self, at: u32) -> Option<&Self::Type> { - self.returns.get(at as usize) - } -} - -impl WasmGlobalType for crate::GlobalType { - type Type = crate::Type; - - fn is_mutable(&self) -> bool { - self.mutable - } - - fn content_type(&self) -> &Self::Type { - &self.content_type - } -} - -impl WasmTableType for crate::TableType { - type Type = crate::Type; - - fn element_type(&self) -> &Self::Type { - &self.element_type - } - - fn initial_limit(&self) -> u32 { - self.limits.initial - } - - fn maximum_limit(&self) -> Option { - self.limits.maximum - } -} - -impl WasmMemoryType for crate::MemoryType { - fn is_shared(&self) -> bool { - self.shared - } - - fn initial_limit(&self) -> u32 { - self.limits.initial - } - fn maximum_limit(&self) -> Option { - self.limits.maximum - } -} - pub enum FunctionEnd { No, Yes, diff --git a/src/validator.rs b/src/validator.rs index e51b362b..9da614ea 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -29,13 +29,15 @@ use crate::primitives::{ Operator, ResizableLimits, Result, SectionCode, TableType, Type, }; -use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder}; - use crate::operators_validator::{ - is_subtype_supertype, FunctionEnd, OperatorValidator, OperatorValidatorConfig, WasmFuncType, - WasmGlobalType, WasmMemoryType, WasmModuleResources, WasmTableType, + is_subtype_supertype, FunctionEnd, OperatorValidator, OperatorValidatorConfig, DEFAULT_OPERATOR_VALIDATOR_CONFIG, }; +use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder}; +use crate::{ + WasmFuncType, WasmGlobalType, WasmMemoryType, + WasmModuleResources, WasmTableType, +}; use crate::{ElemSectionEntryTable, ElementItem}; use crate::readers::FunctionBody; From 4b227e6268a12ef39abb3ababd72127817c3b541 Mon Sep 17 00:00:00 2001 From: Robin Freyler Date: Fri, 17 Jan 2020 11:16:08 +0100 Subject: [PATCH 35/35] apply rustfmt --- src/lib.rs | 5 +---- src/validator.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8be9613e..4a94c47d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,10 +73,7 @@ pub use crate::module_resources::WasmModuleResources; pub use crate::module_resources::WasmTableType; pub use crate::module_resources::WasmType; -pub(crate) use crate::module_resources::{ - wasm_func_type_inputs, - wasm_func_type_outputs, -}; +pub(crate) use crate::module_resources::{wasm_func_type_inputs, wasm_func_type_outputs}; pub use crate::operators_validator::OperatorValidatorConfig; diff --git a/src/validator.rs b/src/validator.rs index 9da614ea..0133248d 100644 --- a/src/validator.rs +++ b/src/validator.rs @@ -34,11 +34,8 @@ use crate::operators_validator::{ DEFAULT_OPERATOR_VALIDATOR_CONFIG, }; use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder}; -use crate::{ - WasmFuncType, WasmGlobalType, WasmMemoryType, - WasmModuleResources, WasmTableType, -}; use crate::{ElemSectionEntryTable, ElementItem}; +use crate::{WasmFuncType, WasmGlobalType, WasmMemoryType, WasmModuleResources, WasmTableType}; use crate::readers::FunctionBody;