summaryrefslogtreecommitdiffstats
path: root/third_party/rust/wasmparser-0.48.2/src/validator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/wasmparser-0.48.2/src/validator.rs')
-rw-r--r--third_party/rust/wasmparser-0.48.2/src/validator.rs946
1 files changed, 946 insertions, 0 deletions
diff --git a/third_party/rust/wasmparser-0.48.2/src/validator.rs b/third_party/rust/wasmparser-0.48.2/src/validator.rs
new file mode 100644
index 0000000000..2fae3112bd
--- /dev/null
+++ b/third_party/rust/wasmparser-0.48.2/src/validator.rs
@@ -0,0 +1,946 @@
+/* Copyright 2018 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.
+ */
+
+use std::collections::HashSet;
+use std::result;
+use std::str;
+
+use crate::limits::{
+ MAX_WASM_FUNCTIONS, MAX_WASM_FUNCTION_LOCALS, MAX_WASM_GLOBALS, MAX_WASM_MEMORIES,
+ MAX_WASM_MEMORY_PAGES, MAX_WASM_TABLES, MAX_WASM_TYPES,
+};
+
+use crate::binary_reader::BinaryReader;
+
+use crate::primitives::{
+ BinaryReaderError, ExternalKind, FuncType, GlobalType, ImportSectionEntryType, MemoryType,
+ Operator, ResizableLimits, Result, SectionCode, TableType, Type,
+};
+
+use crate::operators_validator::{
+ is_subtype_supertype, FunctionEnd, OperatorValidator, OperatorValidatorConfig,
+ DEFAULT_OPERATOR_VALIDATOR_CONFIG,
+};
+use crate::parser::{Parser, ParserInput, ParserState, WasmDecoder};
+use crate::{ElemSectionEntryTable, ElementItem};
+use crate::{WasmFuncType, WasmGlobalType, WasmMemoryType, WasmModuleResources, WasmTableType};
+
+use crate::readers::FunctionBody;
+
+type ValidatorResult<'a, T> = result::Result<T, ParserState<'a>>;
+
+struct InitExpressionState {
+ ty: Type,
+ global_count: usize,
+ function_count: usize,
+ validated: bool,
+}
+
+#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
+enum SectionOrderState {
+ Initial,
+ Type,
+ Import,
+ Function,
+ Table,
+ Memory,
+ Global,
+ Export,
+ Start,
+ Element,
+ DataCount,
+ Code,
+ Data,
+}
+
+impl SectionOrderState {
+ pub fn from_section_code(code: &SectionCode) -> Option<SectionOrderState> {
+ match *code {
+ SectionCode::Type => Some(SectionOrderState::Type),
+ SectionCode::Import => Some(SectionOrderState::Import),
+ SectionCode::Function => Some(SectionOrderState::Function),
+ SectionCode::Table => Some(SectionOrderState::Table),
+ SectionCode::Memory => Some(SectionOrderState::Memory),
+ SectionCode::Global => Some(SectionOrderState::Global),
+ SectionCode::Export => Some(SectionOrderState::Export),
+ SectionCode::Start => Some(SectionOrderState::Start),
+ SectionCode::Element => Some(SectionOrderState::Element),
+ SectionCode::Code => Some(SectionOrderState::Code),
+ SectionCode::Data => Some(SectionOrderState::Data),
+ SectionCode::DataCount => Some(SectionOrderState::DataCount),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Copy, Clone)]
+pub struct ValidatingParserConfig {
+ pub operator_config: OperatorValidatorConfig,
+}
+
+const DEFAULT_VALIDATING_PARSER_CONFIG: ValidatingParserConfig = ValidatingParserConfig {
+ operator_config: DEFAULT_OPERATOR_VALIDATOR_CONFIG,
+};
+
+struct ValidatingParserResources {
+ types: Vec<FuncType>,
+ tables: Vec<TableType>,
+ memories: Vec<MemoryType>,
+ globals: Vec<GlobalType>,
+ element_count: u32,
+ data_count: Option<u32>,
+ func_type_indices: Vec<u32>,
+}
+
+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) -> Option<&Self::FuncType> {
+ self.types.get(at as usize)
+ }
+
+ fn table_at(&self, at: u32) -> Option<&Self::TableType> {
+ self.tables.get(at as usize)
+ }
+
+ fn memory_at(&self, at: u32) -> Option<&Self::MemoryType> {
+ self.memories.get(at as usize)
+ }
+
+ fn global_at(&self, at: u32) -> Option<&Self::GlobalType> {
+ self.globals.get(at as usize)
+ }
+
+ fn func_type_id_at(&self, at: u32) -> Option<u32> {
+ self.func_type_indices.get(at as usize).copied()
+ }
+
+ fn element_count(&self) -> u32 {
+ self.element_count
+ }
+
+ fn data_count(&self) -> u32 {
+ self.data_count.unwrap_or(0)
+ }
+}
+
+pub struct ValidatingParser<'a> {
+ parser: Parser<'a>,
+ validation_error: Option<ParserState<'a>>,
+ read_position: Option<usize>,
+ section_order_state: SectionOrderState,
+ resources: ValidatingParserResources,
+ current_func_index: u32,
+ func_imports_count: u32,
+ init_expression_state: Option<InitExpressionState>,
+ data_found: u32,
+ exported_names: HashSet<String>,
+ current_operator_validator: Option<OperatorValidator>,
+ config: ValidatingParserConfig,
+}
+
+impl<'a> ValidatingParser<'a> {
+ pub fn new(bytes: &[u8], config: Option<ValidatingParserConfig>) -> ValidatingParser {
+ ValidatingParser {
+ parser: Parser::new(bytes),
+ validation_error: None,
+ read_position: None,
+ section_order_state: SectionOrderState::Initial,
+ resources: ValidatingParserResources {
+ types: Vec::new(),
+ tables: Vec::new(),
+ memories: Vec::new(),
+ globals: Vec::new(),
+ element_count: 0,
+ data_count: None,
+ func_type_indices: Vec::new(),
+ },
+ current_func_index: 0,
+ func_imports_count: 0,
+ current_operator_validator: None,
+ init_expression_state: None,
+ data_found: 0,
+ exported_names: HashSet::new(),
+ config: config.unwrap_or(DEFAULT_VALIDATING_PARSER_CONFIG),
+ }
+ }
+
+ pub fn get_resources(
+ &self,
+ ) -> &dyn WasmModuleResources<
+ FuncType = crate::FuncType,
+ TableType = crate::TableType,
+ MemoryType = crate::MemoryType,
+ GlobalType = crate::GlobalType,
+ > {
+ &self.resources
+ }
+
+ fn set_validation_error(&mut self, message: &'static str) {
+ self.validation_error = Some(ParserState::Error(BinaryReaderError {
+ message,
+ offset: self.read_position.unwrap(),
+ }))
+ }
+
+ fn create_error<T>(&self, message: &'static str) -> ValidatorResult<'a, T> {
+ Err(ParserState::Error(BinaryReaderError {
+ message,
+ offset: self.read_position.unwrap(),
+ }))
+ }
+
+ fn check_value_type(&self, ty: Type) -> ValidatorResult<'a, ()> {
+ match ty {
+ Type::I32 | Type::I64 | Type::F32 | Type::F64 => Ok(()),
+ Type::NullRef | Type::AnyFunc | Type::AnyRef => {
+ if !self.config.operator_config.enable_reference_types {
+ return self.create_error("reference types support is not enabled");
+ }
+ Ok(())
+ }
+ Type::V128 => {
+ if !self.config.operator_config.enable_simd {
+ return self.create_error("SIMD support is not enabled");
+ }
+ Ok(())
+ }
+ _ => self.create_error("invalid value type"),
+ }
+ }
+
+ fn check_value_types(&self, types: &[Type]) -> ValidatorResult<'a, ()> {
+ for ty in types {
+ self.check_value_type(*ty)?;
+ }
+ Ok(())
+ }
+
+ fn check_limits(&self, limits: &ResizableLimits) -> ValidatorResult<'a, ()> {
+ if limits.maximum.is_some() && limits.initial > limits.maximum.unwrap() {
+ return self.create_error("maximum limits less than initial");
+ }
+ Ok(())
+ }
+
+ fn check_func_type(&self, func_type: &FuncType) -> ValidatorResult<'a, ()> {
+ if let Type::Func = func_type.form {
+ self.check_value_types(&*func_type.params)?;
+ self.check_value_types(&*func_type.returns)?;
+ if !self.config.operator_config.enable_multi_value && func_type.returns.len() > 1 {
+ self.create_error("func type returns multiple values")
+ } else {
+ Ok(())
+ }
+ } else {
+ self.create_error("type signature is not a func")
+ }
+ }
+
+ fn check_table_type(&self, table_type: &TableType) -> ValidatorResult<'a, ()> {
+ match table_type.element_type {
+ Type::AnyFunc => {}
+ _ => {
+ if !self.config.operator_config.enable_reference_types {
+ return self.create_error("element is not anyfunc");
+ }
+ }
+ }
+ self.check_limits(&table_type.limits)
+ }
+
+ fn check_memory_type(&self, memory_type: &MemoryType) -> ValidatorResult<'a, ()> {
+ self.check_limits(&memory_type.limits)?;
+ let initial = memory_type.limits.initial;
+ if initial as usize > MAX_WASM_MEMORY_PAGES {
+ return self.create_error("memory initial value exceeds limit");
+ }
+ let maximum = memory_type.limits.maximum;
+ if maximum.is_some() && maximum.unwrap() as usize > MAX_WASM_MEMORY_PAGES {
+ return self.create_error("memory maximum value exceeds limit");
+ }
+ Ok(())
+ }
+
+ fn check_global_type(&self, global_type: GlobalType) -> ValidatorResult<'a, ()> {
+ self.check_value_type(global_type.content_type)
+ }
+
+ fn check_import_entry(&self, import_type: &ImportSectionEntryType) -> ValidatorResult<'a, ()> {
+ match *import_type {
+ ImportSectionEntryType::Function(type_index) => {
+ if self.resources.func_type_indices.len() >= MAX_WASM_FUNCTIONS {
+ return self.create_error("functions count out of bounds");
+ }
+ if type_index as usize >= self.resources.types.len() {
+ return self.create_error("type index out of bounds");
+ }
+ Ok(())
+ }
+ ImportSectionEntryType::Table(ref table_type) => {
+ if !self.config.operator_config.enable_reference_types
+ && self.resources.tables.len() >= MAX_WASM_TABLES
+ {
+ return self.create_error("tables count must be at most 1");
+ }
+ self.check_table_type(table_type)
+ }
+ ImportSectionEntryType::Memory(ref memory_type) => {
+ if self.resources.memories.len() >= MAX_WASM_MEMORIES {
+ return self.create_error("memory count must be at most 1");
+ }
+ self.check_memory_type(memory_type)
+ }
+ ImportSectionEntryType::Global(global_type) => {
+ if self.resources.globals.len() >= MAX_WASM_GLOBALS {
+ return self.create_error("functions count out of bounds");
+ }
+ self.check_global_type(global_type)
+ }
+ }
+ }
+
+ fn check_init_expression_operator(&self, operator: &Operator) -> ValidatorResult<'a, ()> {
+ let state = self.init_expression_state.as_ref().unwrap();
+ if state.validated {
+ return self.create_error("only one init_expr operator is expected");
+ }
+ let ty = match *operator {
+ Operator::I32Const { .. } => Type::I32,
+ Operator::I64Const { .. } => Type::I64,
+ Operator::F32Const { .. } => Type::F32,
+ Operator::F64Const { .. } => Type::F64,
+ Operator::RefNull => {
+ if !self.config.operator_config.enable_reference_types {
+ return self.create_error("reference types support is not enabled");
+ }
+ Type::NullRef
+ }
+ Operator::V128Const { .. } => {
+ if !self.config.operator_config.enable_simd {
+ return self.create_error("SIMD support is not enabled");
+ }
+ Type::V128
+ }
+ Operator::GlobalGet { global_index } => {
+ if global_index as usize >= state.global_count {
+ return self.create_error("init_expr global index out of bounds");
+ }
+ self.resources.globals[global_index as usize].content_type
+ }
+ Operator::RefFunc { function_index } => {
+ if function_index as usize >= state.function_count {
+ return self.create_error("init_expr function index out of bounds");
+ }
+ Type::AnyFunc
+ }
+ _ => return self.create_error("invalid init_expr operator"),
+ };
+ if !is_subtype_supertype(ty, state.ty) {
+ return self.create_error("invalid init_expr type");
+ }
+ Ok(())
+ }
+
+ fn check_export_entry(
+ &self,
+ field: &str,
+ kind: ExternalKind,
+ index: u32,
+ ) -> ValidatorResult<'a, ()> {
+ if self.exported_names.contains(field) {
+ return self.create_error("non-unique export name");
+ }
+ match kind {
+ ExternalKind::Function => {
+ if index as usize >= self.resources.func_type_indices.len() {
+ return self.create_error("exported function index out of bounds");
+ }
+ }
+ ExternalKind::Table => {
+ if index as usize >= self.resources.tables.len() {
+ return self.create_error("exported table index out of bounds");
+ }
+ }
+ ExternalKind::Memory => {
+ if index as usize >= self.resources.memories.len() {
+ return self.create_error("exported memory index out of bounds");
+ }
+ }
+ ExternalKind::Global => {
+ if index as usize >= self.resources.globals.len() {
+ return self.create_error("exported global index out of bounds");
+ }
+ }
+ };
+ Ok(())
+ }
+
+ fn check_start(&self, func_index: u32) -> ValidatorResult<'a, ()> {
+ if func_index as usize >= self.resources.func_type_indices.len() {
+ return self.create_error("start function index out of bounds");
+ }
+ let type_index = self.resources.func_type_indices[func_index as usize];
+ let ty = &self.resources.types[type_index as usize];
+ if !ty.params.is_empty() || !ty.returns.is_empty() {
+ return self.create_error("invlid start function type");
+ }
+ Ok(())
+ }
+
+ fn process_begin_section(&self, code: &SectionCode) -> ValidatorResult<'a, SectionOrderState> {
+ let order_state = SectionOrderState::from_section_code(code);
+ Ok(match self.section_order_state {
+ SectionOrderState::Initial => match order_state {
+ Some(section) => section,
+ _ => SectionOrderState::Initial,
+ },
+ previous => {
+ if let Some(order_state_unwraped) = order_state {
+ if previous >= order_state_unwraped {
+ return self.create_error("section out of order");
+ }
+ order_state_unwraped
+ } else {
+ previous
+ }
+ }
+ })
+ }
+
+ fn process_state(&mut self) {
+ match *self.parser.last_state() {
+ ParserState::BeginWasm { version } => {
+ if version != 1 {
+ self.set_validation_error("bad wasm file version");
+ }
+ }
+ ParserState::BeginSection { ref code, .. } => {
+ let check = self.process_begin_section(code);
+ if check.is_err() {
+ self.validation_error = check.err();
+ } else {
+ self.section_order_state = check.ok().unwrap();
+ }
+ }
+ ParserState::TypeSectionEntry(ref func_type) => {
+ let check = self.check_func_type(func_type);
+ if check.is_err() {
+ self.validation_error = check.err();
+ } else if self.resources.types.len() > MAX_WASM_TYPES {
+ self.set_validation_error("types count is out of bounds");
+ } else {
+ self.resources.types.push(func_type.clone());
+ }
+ }
+ ParserState::ImportSectionEntry { ref ty, .. } => {
+ let check = self.check_import_entry(ty);
+ if check.is_err() {
+ self.validation_error = check.err();
+ } else {
+ match *ty {
+ ImportSectionEntryType::Function(type_index) => {
+ self.func_imports_count += 1;
+ self.resources.func_type_indices.push(type_index);
+ }
+ ImportSectionEntryType::Table(ref table_type) => {
+ self.resources.tables.push(table_type.clone());
+ }
+ ImportSectionEntryType::Memory(ref memory_type) => {
+ self.resources.memories.push(memory_type.clone());
+ }
+ ImportSectionEntryType::Global(ref global_type) => {
+ self.resources.globals.push(global_type.clone());
+ }
+ }
+ }
+ }
+ ParserState::FunctionSectionEntry(type_index) => {
+ if type_index as usize >= self.resources.types.len() {
+ self.set_validation_error("func type index out of bounds");
+ } else if self.resources.func_type_indices.len() >= MAX_WASM_FUNCTIONS {
+ self.set_validation_error("functions count out of bounds");
+ } else {
+ self.resources.func_type_indices.push(type_index);
+ }
+ }
+ ParserState::TableSectionEntry(ref table_type) => {
+ if !self.config.operator_config.enable_reference_types
+ && self.resources.tables.len() >= MAX_WASM_TABLES
+ {
+ self.set_validation_error("tables count must be at most 1");
+ } else {
+ self.validation_error = self.check_table_type(table_type).err();
+ self.resources.tables.push(table_type.clone());
+ }
+ }
+ ParserState::MemorySectionEntry(ref memory_type) => {
+ if self.resources.memories.len() >= MAX_WASM_MEMORIES {
+ self.set_validation_error("memories count must be at most 1");
+ } else {
+ self.validation_error = self.check_memory_type(memory_type).err();
+ self.resources.memories.push(memory_type.clone());
+ }
+ }
+ ParserState::BeginGlobalSectionEntry(global_type) => {
+ if self.resources.globals.len() >= MAX_WASM_GLOBALS {
+ self.set_validation_error("globals count out of bounds");
+ } else {
+ self.validation_error = self.check_global_type(global_type).err();
+ self.init_expression_state = Some(InitExpressionState {
+ ty: global_type.content_type,
+ global_count: self.resources.globals.len(),
+ function_count: self.resources.func_type_indices.len(),
+ validated: false,
+ });
+ self.resources.globals.push(global_type);
+ }
+ }
+ ParserState::BeginInitExpressionBody => {
+ assert!(self.init_expression_state.is_some());
+ }
+ ParserState::InitExpressionOperator(ref operator) => {
+ self.validation_error = self.check_init_expression_operator(operator).err();
+ self.init_expression_state.as_mut().unwrap().validated = true;
+ }
+ ParserState::EndInitExpressionBody => {
+ if !self.init_expression_state.as_ref().unwrap().validated {
+ self.set_validation_error("init_expr is empty");
+ }
+ self.init_expression_state = None;
+ }
+ ParserState::ExportSectionEntry { field, kind, index } => {
+ self.validation_error = self.check_export_entry(field, kind, index).err();
+ self.exported_names.insert(String::from(field));
+ }
+ ParserState::StartSectionEntry(func_index) => {
+ self.validation_error = self.check_start(func_index).err();
+ }
+ ParserState::DataCountSectionEntry(count) => {
+ self.resources.data_count = Some(count);
+ }
+ ParserState::BeginElementSectionEntry { table, ty } => {
+ self.resources.element_count += 1;
+ if let ElemSectionEntryTable::Active(table_index) = table {
+ let table = match self.resources.tables.get(table_index as usize) {
+ Some(t) => t,
+ None => {
+ self.set_validation_error("element section table index out of bounds");
+ return;
+ }
+ };
+ if !is_subtype_supertype(ty, table.element_type) {
+ self.set_validation_error("element_type != table type");
+ return;
+ }
+ if !self.config.operator_config.enable_reference_types && ty != Type::AnyFunc {
+ self.set_validation_error("element_type != anyfunc is not supported yet");
+ return;
+ }
+ self.init_expression_state = Some(InitExpressionState {
+ ty: Type::I32,
+ global_count: self.resources.globals.len(),
+ function_count: self.resources.func_type_indices.len(),
+ validated: false,
+ });
+ }
+ }
+ ParserState::ElementSectionEntryBody(ref indices) => {
+ for item in &**indices {
+ if let ElementItem::Func(func_index) = item {
+ if *func_index as usize >= self.resources.func_type_indices.len() {
+ self.set_validation_error("element func index out of bounds");
+ break;
+ }
+ }
+ }
+ }
+ ParserState::BeginFunctionBody { .. } => {
+ let index = (self.current_func_index + self.func_imports_count) as usize;
+ if index as usize >= self.resources.func_type_indices.len() {
+ self.set_validation_error("func type is not defined");
+ }
+ }
+ ParserState::FunctionBodyLocals { ref locals } => {
+ let index = (self.current_func_index + self.func_imports_count) as usize;
+ let func_type =
+ &self.resources.types[self.resources.func_type_indices[index] as usize];
+ let operator_config = self.config.operator_config;
+ self.current_operator_validator =
+ Some(OperatorValidator::new(func_type, locals, operator_config));
+ }
+ ParserState::CodeOperator(ref operator) => {
+ let check = self
+ .current_operator_validator
+ .as_mut()
+ .unwrap()
+ .process_operator(operator, &self.resources);
+
+ if let Err(err) = check {
+ self.set_validation_error(err);
+ }
+ }
+ ParserState::EndFunctionBody => {
+ let check = self
+ .current_operator_validator
+ .as_ref()
+ .unwrap()
+ .process_end_function();
+ if let Err(err) = check {
+ self.set_validation_error(err);
+ }
+ self.current_func_index += 1;
+ self.current_operator_validator = None;
+ }
+ ParserState::BeginDataSectionEntryBody(_) => {
+ self.data_found += 1;
+ }
+ ParserState::BeginActiveDataSectionEntry(memory_index) => {
+ if memory_index as usize >= self.resources.memories.len() {
+ self.set_validation_error("data section memory index out of bounds");
+ } else {
+ self.init_expression_state = Some(InitExpressionState {
+ ty: Type::I32,
+ global_count: self.resources.globals.len(),
+ function_count: self.resources.func_type_indices.len(),
+ validated: false,
+ });
+ }
+ }
+ ParserState::EndWasm => {
+ if self.resources.func_type_indices.len()
+ != self.current_func_index as usize + self.func_imports_count as usize
+ {
+ self.set_validation_error(
+ "function and code section have inconsistent lengths",
+ );
+ }
+ if let Some(data_count) = self.resources.data_count {
+ if data_count != self.data_found {
+ self.set_validation_error("data count section and passive data mismatch");
+ }
+ }
+ }
+ _ => (),
+ };
+ }
+
+ pub fn create_validating_operator_parser<'b>(&mut self) -> ValidatingOperatorParser<'b>
+ where
+ 'a: 'b,
+ {
+ let func_body_offset = match *self.last_state() {
+ ParserState::BeginFunctionBody { .. } => self.parser.current_position(),
+ _ => panic!("Invalid reader state"),
+ };
+ self.read();
+ let operator_validator = match *self.last_state() {
+ ParserState::FunctionBodyLocals { ref locals } => {
+ let index = (self.current_func_index + self.func_imports_count) as usize;
+ let func_type =
+ &self.resources.types[self.resources.func_type_indices[index] as usize];
+ let operator_config = self.config.operator_config;
+ OperatorValidator::new(func_type, locals, operator_config)
+ }
+ _ => panic!("Invalid reader state"),
+ };
+ let reader = self.create_binary_reader();
+ ValidatingOperatorParser::new(operator_validator, reader, func_body_offset)
+ }
+}
+
+impl<'a> WasmDecoder<'a> for ValidatingParser<'a> {
+ fn read(&mut self) -> &ParserState<'a> {
+ if self.validation_error.is_some() {
+ panic!("Parser in error state: validation");
+ }
+ self.read_position = Some(self.parser.current_position());
+ self.parser.read();
+ self.process_state();
+ self.last_state()
+ }
+
+ fn push_input(&mut self, input: ParserInput) {
+ match input {
+ ParserInput::SkipSection => panic!("Not supported"),
+ ParserInput::ReadSectionRawData => panic!("Not supported"),
+ ParserInput::SkipFunctionBody => {
+ self.current_func_index += 1;
+ self.parser.push_input(input);
+ }
+ _ => self.parser.push_input(input),
+ }
+ }
+
+ fn read_with_input(&mut self, input: ParserInput) -> &ParserState<'a> {
+ self.push_input(input);
+ self.read()
+ }
+
+ fn create_binary_reader<'b>(&mut self) -> BinaryReader<'b>
+ where
+ 'a: 'b,
+ {
+ if let ParserState::BeginSection { .. } = *self.parser.last_state() {
+ panic!("Not supported");
+ }
+ self.parser.create_binary_reader()
+ }
+
+ fn last_state(&self) -> &ParserState<'a> {
+ if self.validation_error.is_some() {
+ self.validation_error.as_ref().unwrap()
+ } else {
+ self.parser.last_state()
+ }
+ }
+}
+
+pub struct ValidatingOperatorParser<'b> {
+ operator_validator: OperatorValidator,
+ reader: BinaryReader<'b>,
+ func_body_offset: usize,
+ end_function: bool,
+}
+
+impl<'b> ValidatingOperatorParser<'b> {
+ pub(crate) fn new<'c>(
+ operator_validator: OperatorValidator,
+ reader: BinaryReader<'c>,
+ func_body_offset: usize,
+ ) -> ValidatingOperatorParser<'c>
+ where
+ 'b: 'c,
+ {
+ ValidatingOperatorParser {
+ operator_validator,
+ reader,
+ func_body_offset,
+ end_function: false,
+ }
+ }
+
+ pub fn eof(&self) -> bool {
+ self.end_function
+ }
+
+ pub fn current_position(&self) -> usize {
+ self.reader.current_position()
+ }
+
+ pub fn is_dead_code(&self) -> bool {
+ self.operator_validator.is_dead_code()
+ }
+
+ /// Creates a BinaryReader when current state is ParserState::BeginSection
+ /// or ParserState::BeginFunctionBody.
+ ///
+ /// # Examples
+ /// ```
+ /// # let data = &[0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0, 0x1, 0x84,
+ /// # 0x80, 0x80, 0x80, 0x0, 0x1, 0x60, 0x0, 0x0, 0x3, 0x83,
+ /// # 0x80, 0x80, 0x80, 0x0, 0x2, 0x0, 0x0, 0x6, 0x81, 0x80,
+ /// # 0x80, 0x80, 0x0, 0x0, 0xa, 0x91, 0x80, 0x80, 0x80, 0x0,
+ /// # 0x2, 0x83, 0x80, 0x80, 0x80, 0x0, 0x0, 0x1, 0xb, 0x83,
+ /// # 0x80, 0x80, 0x80, 0x0, 0x0, 0x0, 0xb];
+ /// use wasmparser::{WasmDecoder, ParserState, ValidatingParser};
+ /// let mut parser = ValidatingParser::new(data, None);
+ /// let mut i = 0;
+ /// loop {
+ /// {
+ /// match *parser.read() {
+ /// ParserState::Error(_) |
+ /// ParserState::EndWasm => break,
+ /// ParserState::BeginFunctionBody {..} => (),
+ /// _ => continue
+ /// }
+ /// }
+ /// let mut reader = parser.create_validating_operator_parser();
+ /// println!("Function {}", i);
+ /// i += 1;
+ /// while !reader.eof() {
+ /// let read = reader.next(parser.get_resources());
+ /// if let Ok(ref op) = read {
+ /// println!(" {:?}", op);
+ /// } else {
+ /// panic!(" Bad wasm code {:?}", read.err());
+ /// }
+ /// }
+ /// }
+ /// ```
+ 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<Operator<'c>>
+ where
+ 'b: 'c,
+ {
+ let op = self.reader.read_operator()?;
+ match self.operator_validator.process_operator(&op, resources) {
+ Err(err) => {
+ return Err(BinaryReaderError {
+ message: err,
+ offset: self.func_body_offset + self.reader.current_position(),
+ });
+ }
+ Ok(FunctionEnd::Yes) => {
+ self.end_function = true;
+ if !self.reader.eof() {
+ return Err(BinaryReaderError {
+ message: "unexpected end of function",
+ offset: self.func_body_offset + self.reader.current_position(),
+ });
+ }
+ }
+ _ => (),
+ };
+ Ok(op)
+ }
+}
+
+/// 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<
+ F: WasmFuncType,
+ T: WasmTableType,
+ M: WasmMemoryType,
+ G: WasmGlobalType,
+>(
+ bytes: &[u8],
+ offset: usize,
+ func_index: u32,
+ resources: &dyn WasmModuleResources<
+ FuncType = F,
+ TableType = T,
+ MemoryType = M,
+ GlobalType = G,
+ >,
+ operator_config: Option<OperatorValidatorConfig>,
+) -> Result<()> {
+ let operator_config = operator_config.unwrap_or(DEFAULT_OPERATOR_VALIDATOR_CONFIG);
+ let function_body = FunctionBody::new(offset, bytes);
+ let mut locals_reader = function_body.get_locals_reader()?;
+ let local_count = locals_reader.get_count() as usize;
+ if local_count > MAX_WASM_FUNCTION_LOCALS {
+ return Err(BinaryReaderError {
+ message: "locals exceed maximum",
+ offset: locals_reader.original_position(),
+ });
+ }
+ let mut locals: Vec<(u32, Type)> = Vec::with_capacity(local_count);
+ let mut locals_total: usize = 0;
+ for _ in 0..local_count {
+ let (count, ty) = locals_reader.read()?;
+ locals_total =
+ locals_total
+ .checked_add(count as usize)
+ .ok_or_else(|| BinaryReaderError {
+ message: "locals overflow",
+ offset: locals_reader.original_position(),
+ })?;
+ if locals_total > MAX_WASM_FUNCTION_LOCALS {
+ return Err(BinaryReaderError {
+ message: "locals exceed maximum",
+ offset: locals_reader.original_position(),
+ });
+ }
+ locals.push((count, ty));
+ }
+ let operators_reader = function_body.get_operators_reader()?;
+ let func_type_index = resources
+ .func_type_id_at(func_index)
+ // Note: This was an out-of-bounds access before the change to return `Option`
+ // so I assumed it is considered a bug to access a non-existing function
+ // id here and went with panicking instead of returning a proper error.
+ .expect("the function index of the validated function itself is out of bounds");
+ let func_type = resources
+ .type_at(func_type_index)
+ // 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;
+ for item in operators_reader.into_iter_with_offsets() {
+ let (ref op, offset) = item?;
+ match operator_validator
+ .process_operator(op, resources)
+ .map_err(|message| BinaryReaderError { message, offset })?
+ {
+ FunctionEnd::Yes => {
+ eof_found = true;
+ }
+ FunctionEnd::No => {
+ last_op = offset;
+ }
+ }
+ }
+ if !eof_found {
+ return Err(BinaryReaderError {
+ message: "end of function not found",
+ offset: last_op,
+ });
+ }
+ Ok(())
+}
+
+/// Test whether the given buffer contains a valid WebAssembly module,
+/// analogous to WebAssembly.validate in the JS API.
+pub fn validate(bytes: &[u8], config: Option<ValidatingParserConfig>) -> Result<()> {
+ let mut parser = ValidatingParser::new(bytes, config);
+ let mut parser_input = None;
+ let mut func_ranges = Vec::new();
+ loop {
+ let next_input = parser_input.take().unwrap_or(ParserInput::Default);
+ let state = parser.read_with_input(next_input);
+ match *state {
+ ParserState::EndWasm => break,
+ ParserState::Error(e) => return Err(e),
+ ParserState::BeginFunctionBody { range } => {
+ parser_input = Some(ParserInput::SkipFunctionBody);
+ func_ranges.push(range);
+ }
+ _ => (),
+ }
+ }
+ let operator_config = config.map(|c| c.operator_config);
+ for (i, range) in func_ranges.into_iter().enumerate() {
+ let function_body = range.slice(bytes);
+ let function_index = i as u32 + parser.func_imports_count;
+ validate_function_body(
+ function_body,
+ range.start,
+ function_index,
+ &parser.resources,
+ operator_config,
+ )?;
+ }
+ Ok(())
+}
+
+#[test]
+fn test_validate() {
+ assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0], None).is_ok());
+ assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x2, 0x0, 0x0, 0x0], None).is_err());
+}