diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/wasmparser/src/validator.rs | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/wasmparser/src/validator.rs')
-rw-r--r-- | third_party/rust/wasmparser/src/validator.rs | 1845 |
1 files changed, 1845 insertions, 0 deletions
diff --git a/third_party/rust/wasmparser/src/validator.rs b/third_party/rust/wasmparser/src/validator.rs new file mode 100644 index 0000000000..fe83a3f326 --- /dev/null +++ b/third_party/rust/wasmparser/src/validator.rs @@ -0,0 +1,1845 @@ +/* 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 crate::limits::*; +use crate::ResizableLimits64; +use crate::WasmModuleResources; +use crate::{Alias, AliasedInstance, ExternalKind, Import, ImportSectionEntryType}; +use crate::{BinaryReaderError, GlobalType, MemoryType, Range, Result, TableType, Type}; +use crate::{DataKind, ElementItem, ElementKind, InitExpr, Instance, Operator}; +use crate::{Export, ExportType, FunctionBody, Parser, Payload}; +use crate::{FuncType, ResizableLimits, SectionReader, SectionWithLimitedItems}; +use std::collections::{HashMap, HashSet}; +use std::mem; +use std::sync::Arc; + +/// Test whether the given buffer contains a valid WebAssembly module, +/// analogous to [`WebAssembly.validate`][js] in the JS API. +/// +/// This functions requires the wasm module is entirely resident in memory and +/// is specified by `bytes`. Additionally this validates the given bytes with +/// the default set of WebAssembly features implemented by `wasmparser`. +/// +/// For more fine-tuned control over validation it's recommended to review the +/// documentation of [`Validator`]. +/// +/// [js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/validate +pub fn validate(bytes: &[u8]) -> Result<()> { + Validator::new().validate_all(bytes) +} + +#[test] +fn test_validate() { + assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0]).is_ok()); + assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x2, 0x0, 0x0, 0x0]).is_err()); +} + +mod func; +pub use func::FuncValidator; + +/// Validator for a WebAssembly binary module. +/// +/// This structure encapsulates state necessary to validate a WebAssembly +/// binary. This implements validation as defined by the [core +/// specification][core]. A `Validator` is designed, like +/// [`Parser`], to accept incremental input over time. +/// Additionally a `Validator` is also designed for parallel validation of +/// functions as they are received. +/// +/// It's expected that you'll be using a [`Parser`] in tandem with a +/// `Validator`. As each [`Payload`](crate::Payload) is received from a +/// [`Parser`] you'll pass it into a `Validator` to test the validity of the +/// payload. Note that all payloads received from a [`Parser`] are expected to +/// be passed to a [`Validator`]. For example if you receive +/// [`Payload::TypeSection`](crate::Payload) you'll call +/// [`Validator::type_section`] to validate this. +/// +/// The design of [`Validator`] is intended that you'll interleave, in your own +/// application's processing, calls to validation. Each variant, after it's +/// received, will be validated and then your application would proceed as +/// usual. At all times, however, you'll have access to the [`Validator`] and +/// the validation context up to that point. This enables applications to check +/// the types of functions and learn how many globals there are, for example. +/// +/// [core]: https://webassembly.github.io/spec/core/valid/index.html +#[derive(Default)] +pub struct Validator { + /// Internal state that is incrementally built-up for the module being + /// validate. This houses type information for all wasm items, like + /// functions. Note that this starts out as a solely owned `Arc<T>` so we can + /// get mutable access, but after we get to the code section this is never + /// mutated to we can clone it cheaply and hand it to sub-validators. + state: arc::MaybeOwned<ModuleState>, + + /// Enabled WebAssembly feature flags, dictating what's valid and what + /// isn't. + features: WasmFeatures, + + /// Where we are, order-wise, in the wasm binary. + order: Order, + + /// The current byte-level offset in the wasm binary. This is updated to + /// produce error messages in `create_error`. + offset: usize, + + /// The number of data segments we ended up finding in this module, or 0 if + /// they either weren't present or none were found. + data_found: u32, + + /// The number of functions we expect to be defined in the code section, or + /// basically the length of the function section if it was found. The next + /// index is where we are, in the function index space, for the next entry + /// in the code section (used to figure out what type is next for the + /// function being validated. + expected_code_bodies: Option<u32>, + code_section_index: usize, + + /// Similar to code bodies above, but for module bodies instead. + expected_modules: Option<u32>, + module_code_section_index: usize, + + /// If this validator is for a nested module then this keeps track of the + /// type of the module that we're matching against. The `expected_type` is + /// an entry in our parent's type index space, and the two positional + /// indices keep track of where we are in matching against imports/exports. + /// + /// Note that the exact algorithm for how it's determine that a submodule + /// matches its declare type is a bit up for debate. For now we go for 1:1 + /// "everything must be equal" matching. This is the subject of + /// WebAssembly/module-linking#7, though. + expected_type: Option<Def<u32>>, + expected_import_pos: usize, + expected_export_pos: usize, +} + +#[derive(Default)] +struct ModuleState { + depth: usize, + types: Vec<ValidatedType>, + tables: Vec<Def<TableType>>, + memories: Vec<MemoryType>, + globals: Vec<Def<GlobalType>>, + element_types: Vec<Type>, + data_count: Option<u32>, + func_type_indices: Vec<Def<u32>>, + module_type_indices: Vec<Def<u32>>, + instance_type_indices: Vec<Def<InstanceDef>>, + function_references: HashSet<u32>, + parent: Option<Arc<ModuleState>>, +} + +/// Flags for features that are enabled for validation. +#[derive(Hash, Debug, Copy, Clone)] +pub struct WasmFeatures { + /// The WebAssembly reference types proposal + pub reference_types: bool, + /// The WebAssembly module linking proposal + pub module_linking: bool, + /// The WebAssembly SIMD proposal + pub simd: bool, + /// The WebAssembly multi-value proposal (enabled by default) + pub multi_value: bool, + /// The WebAssembly threads proposal + pub threads: bool, + /// The WebAssembly tail-call proposal + pub tail_call: bool, + /// The WebAssembly bulk memory operations proposal + pub bulk_memory: bool, + /// Whether or not only deterministic instructions are allowed + pub deterministic_only: bool, + /// The WebAssembly multi memory proposal + pub multi_memory: bool, + /// The WebAssembly memory64 proposal + pub memory64: bool, +} + +impl Default for WasmFeatures { + fn default() -> WasmFeatures { + WasmFeatures { + // off-by-default features + reference_types: false, + module_linking: false, + simd: false, + threads: false, + tail_call: false, + bulk_memory: false, + multi_memory: false, + memory64: false, + deterministic_only: cfg!(feature = "deterministic"), + + // on-by-default features + multi_value: true, + } + } +} + +#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)] +enum Order { + Initial, + AfterHeader, + Type, + Import, + ModuleLinkingHeader, + Function, + Table, + Memory, + Global, + Export, + Start, + Element, + DataCount, + ModuleCode, + Code, + Data, +} + +impl Default for Order { + fn default() -> Order { + Order::Initial + } +} + +enum InstanceDef { + Imported { type_idx: u32 }, + Instantiated { module_idx: u32 }, +} + +enum ValidatedType { + Def(TypeDef), + Alias(Def<u32>), +} + +enum TypeDef { + Func(FuncType), + Module(ModuleType), + Instance(InstanceType), +} + +struct ModuleType { + imports: Vec<(String, Option<String>, ImportSectionEntryType)>, + exports: Vec<(String, ImportSectionEntryType)>, +} + +struct InstanceType { + exports: Vec<(String, ImportSectionEntryType)>, +} + +fn to_import(item: &(String, Option<String>, ImportSectionEntryType)) -> Import<'_> { + Import { + module: &item.0, + field: item.1.as_deref(), + ty: item.2, + } +} + +fn to_export(item: &(String, ImportSectionEntryType)) -> ExportType<'_> { + ExportType { + name: &item.0, + ty: item.1, + } +} + +/// Possible return values from [`Validator::payload`]. +pub enum ValidPayload<'a> { + /// The payload validated, no further action need be taken. + Ok, + /// The payload validated, but it started a nested module. + /// + /// This result indicates that the current validator needs to be saved until + /// later. The returned parser and validator should be used instead. + Push(Parser, Validator), + /// The payload validated, and the current validator is finished. The last + /// validator that was in use should be popped off the stack to resume. + Pop, + /// A function was found to be validate. + Func(FuncValidator<ValidatorResources>, FunctionBody<'a>), +} + +impl Validator { + /// Creates a new [`Validator`] ready to validate a WebAssembly module. + /// + /// The new validator will receive payloads parsed from + /// [`Parser`], and expects the first payload received to be + /// the version header from the parser. + pub fn new() -> Validator { + Validator::default() + } + + /// Configures the enabled WebAssembly features for this `Validator`. + pub fn wasm_features(&mut self, features: WasmFeatures) -> &mut Validator { + self.features = features; + self + } + + /// Validates an entire in-memory module with this validator. + /// + /// This function will internally create a [`Parser`] to parse the `bytes` + /// provided. The entire wasm module specified by `bytes` will be parsed and + /// validated. Parse and validation errors will be returned through + /// `Err(_)`, and otherwise a successful validation means `Ok(())` is + /// returned. + pub fn validate_all(self, bytes: &[u8]) -> Result<()> { + let mut functions_to_validate = Vec::new(); + let mut stack = Vec::new(); + let mut cur = self; + for payload in Parser::new(0).parse_all(bytes) { + match cur.payload(&payload?)? { + ValidPayload::Ok => {} + ValidPayload::Pop => cur = stack.pop().unwrap(), + ValidPayload::Push(_parser, validator) => { + stack.push(cur); + cur = validator + } + ValidPayload::Func(validator, ops) => functions_to_validate.push((validator, ops)), + } + } + + for (mut validator, body) in functions_to_validate { + validator.validate(&body)?; + } + Ok(()) + } + + /// Convenience function to validate a single [`Payload`]. + /// + /// This function is intended to be used as a convenience. It will + /// internally perform any validation necessary to validate the [`Payload`] + /// provided. The convenience part is that you're likely already going to + /// be matching on [`Payload`] in your application, at which point it's more + /// appropriate to call the individual methods on [`Validator`] per-variant + /// in [`Payload`], such as [`Validator::type_section`]. + /// + /// This function returns a [`ValidPayload`] variant on success, indicating + /// one of a few possible actions that need to be taken after a payload is + /// validated. For example function contents are not validated here, they're + /// returned through [`ValidPayload`] for validation by the caller. + pub fn payload<'a>(&mut self, payload: &Payload<'a>) -> Result<ValidPayload<'a>> { + use crate::Payload::*; + match payload { + Version { num, range } => self.version(*num, range)?, + TypeSection(s) => self.type_section(s)?, + ImportSection(s) => self.import_section(s)?, + AliasSection(s) => self.alias_section(s)?, + InstanceSection(s) => self.instance_section(s)?, + ModuleSection(s) => self.module_section(s)?, + FunctionSection(s) => self.function_section(s)?, + TableSection(s) => self.table_section(s)?, + MemorySection(s) => self.memory_section(s)?, + GlobalSection(s) => self.global_section(s)?, + ExportSection(s) => self.export_section(s)?, + StartSection { func, range } => self.start_section(*func, range)?, + ElementSection(s) => self.element_section(s)?, + DataCountSection { count, range } => self.data_count_section(*count, range)?, + CodeSectionStart { + count, + range, + size: _, + } => self.code_section_start(*count, range)?, + CodeSectionEntry(body) => { + let func_validator = self.code_section_entry()?; + return Ok(ValidPayload::Func(func_validator, body.clone())); + } + ModuleCodeSectionStart { + count, + range, + size: _, + } => self.module_code_section_start(*count, range)?, + DataSection(s) => self.data_section(s)?, + End => { + self.end()?; + return Ok(if self.state.depth > 0 { + ValidPayload::Pop + } else { + ValidPayload::Ok + }); + } + + CustomSection { .. } => {} // no validation for custom sections + UnknownSection { id, range, .. } => self.unknown_section(*id, range)?, + ModuleCodeSectionEntry { parser, range: _ } => { + let subvalidator = self.module_code_section_entry(); + return Ok(ValidPayload::Push(parser.clone(), subvalidator)); + } + } + Ok(ValidPayload::Ok) + } + + fn create_error<T>(&self, msg: impl Into<String>) -> Result<T> { + Err(BinaryReaderError::new(msg.into(), self.offset)) + } + + /// Validates [`Payload::Version`](crate::Payload) + pub fn version(&mut self, num: u32, range: &Range) -> Result<()> { + self.offset = range.start; + if self.order != Order::Initial { + return self.create_error("wasm version header out of order"); + } + self.order = Order::AfterHeader; + if num != 1 { + return self.create_error("bad wasm file version"); + } + Ok(()) + } + + fn update_order(&mut self, order: Order) -> Result<()> { + let prev = mem::replace(&mut self.order, order); + // If the previous section came before this section, then that's always + // valid. + if prev < order { + return Ok(()); + } + // ... otherwise if this is a repeated section then only the "module + // linking header" is allows to have repeats + if prev == self.order && self.order == Order::ModuleLinkingHeader { + return Ok(()); + } + self.create_error("section out of order") + } + + fn header_order(&mut self, order: Order) -> Order { + if self.features.module_linking { + Order::ModuleLinkingHeader + } else { + order + } + } + + fn get_type<'me>(&'me self, idx: Def<u32>) -> Result<Def<&'me TypeDef>> { + match self.state.get_type(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown type: type index out of bounds"), + } + } + + fn get_table<'me>(&'me self, idx: Def<u32>) -> Result<&'me Def<TableType>> { + match self.state.get_table(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown table: table index out of bounds"), + } + } + + fn get_memory<'me>(&'me self, idx: Def<u32>) -> Result<&'me MemoryType> { + match self.state.get_memory(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown memory: memory index out of bounds"), + } + } + + fn get_global<'me>(&'me self, idx: Def<u32>) -> Result<&'me Def<GlobalType>> { + match self.state.get_global(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown global: global index out of bounds"), + } + } + + fn get_func_type_index<'me>(&'me self, idx: Def<u32>) -> Result<Def<u32>> { + match self.state.get_func_type_index(idx) { + Some(t) => Ok(t), + None => self.create_error(format!( + "unknown function {}: func index out of bounds", + idx.item + )), + } + } + + fn get_module_type_index<'me>(&'me self, idx: Def<u32>) -> Result<Def<u32>> { + match self.state.get_module_type_index(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown module: module index out of bounds"), + } + } + + fn get_instance_def<'me>(&'me self, idx: Def<u32>) -> Result<&'me Def<InstanceDef>> { + match self.state.get_instance_def(idx) { + Some(t) => Ok(t), + None => self.create_error("unknown instance: instance index out of bounds"), + } + } + + fn func_type_at<'me>(&'me self, type_index: Def<u32>) -> Result<Def<&'me FuncType>> { + let def = self.get_type(type_index)?; + match &def.item { + TypeDef::Func(item) => Ok(def.with(item)), + _ => self.create_error("type index is not a function"), + } + } + + fn module_type_at<'me>(&'me self, type_index: Def<u32>) -> Result<Def<&'me ModuleType>> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let ty = self.get_type(type_index)?; + match &ty.item { + TypeDef::Module(item) => Ok(ty.with(item)), + _ => self.create_error("type index is not a module"), + } + } + + fn instance_type_at<'me>(&'me self, type_index: Def<u32>) -> Result<Def<&'me InstanceType>> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let def = self.get_type(type_index)?; + match &def.item { + TypeDef::Instance(item) => Ok(def.with(item)), + _ => self.create_error("type index is not an instance"), + } + } + + fn check_max(&self, cur_len: usize, amt_added: u32, max: usize, desc: &str) -> Result<()> { + let overflow = max + .checked_sub(cur_len) + .and_then(|amt| amt.checked_sub(amt_added as usize)) + .is_none(); + if overflow { + return if max == 1 { + self.create_error(format!("multiple {}", desc)) + } else { + self.create_error(format!("{} count is out of bounds", desc)) + }; + } + Ok(()) + } + + fn section<T>( + &mut self, + order: Order, + section: &T, + mut validate_item: impl FnMut(&mut Self, T::Item) -> Result<()>, + ) -> Result<()> + where + T: SectionReader + Clone + SectionWithLimitedItems, + { + self.offset = section.range().start; + self.update_order(order)?; + + let mut section = section.clone(); + for _ in 0..section.get_count() { + self.offset = section.original_position(); + let item = section.read()?; + validate_item(self, item)?; + } + self.offset = section.range().end; + section.ensure_end()?; + Ok(()) + } + + /// Validates [`Payload::TypeSection`](crate::Payload) + pub fn type_section(&mut self, section: &crate::TypeSectionReader<'_>) -> Result<()> { + let order = self.header_order(Order::Type); + self.check_max( + self.state.types.len(), + section.get_count(), + MAX_WASM_TYPES, + "types", + )?; + self.section(order, section, |me, item| me.type_def(item)) + } + + fn type_def(&mut self, def: crate::TypeDef<'_>) -> Result<()> { + let def = match def { + crate::TypeDef::Func(t) => { + for ty in t.params.iter().chain(t.returns.iter()) { + self.value_type(*ty)?; + } + if t.returns.len() > 1 && !self.features.multi_value { + return self + .create_error("invalid result arity: func type returns multiple values"); + } + TypeDef::Func(t) + } + crate::TypeDef::Module(t) => { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let imports = t + .imports + .iter() + .map(|i| { + self.import_entry_type(&i.ty)?; + Ok((i.module.to_string(), i.field.map(|i| i.to_string()), i.ty)) + }) + .collect::<Result<_>>()?; + let mut names = HashSet::new(); + let exports = t + .exports + .iter() + .map(|e| { + if !names.insert(e.name) { + return self.create_error("duplicate export name"); + } + self.import_entry_type(&e.ty)?; + Ok((e.name.to_string(), e.ty)) + }) + .collect::<Result<_>>()?; + TypeDef::Module(ModuleType { imports, exports }) + } + crate::TypeDef::Instance(t) => { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + let mut names = HashSet::new(); + let exports = t + .exports + .iter() + .map(|e| { + if !names.insert(e.name) { + return self.create_error("duplicate export name"); + } + self.import_entry_type(&e.ty)?; + Ok((e.name.to_string(), e.ty)) + }) + .collect::<Result<_>>()?; + TypeDef::Instance(InstanceType { exports }) + } + }; + let def = ValidatedType::Def(def); + self.state.assert_mut().types.push(def); + Ok(()) + } + + fn value_type(&self, ty: Type) -> Result<()> { + match self.features.check_value_type(ty) { + Ok(()) => Ok(()), + Err(e) => self.create_error(e), + } + } + + fn import_entry_type(&self, import_type: &ImportSectionEntryType) -> Result<()> { + match import_type { + ImportSectionEntryType::Function(type_index) => { + self.func_type_at(self.state.def(*type_index))?; + Ok(()) + } + ImportSectionEntryType::Table(t) => self.table_type(t), + ImportSectionEntryType::Memory(t) => self.memory_type(t), + ImportSectionEntryType::Global(t) => self.global_type(t), + ImportSectionEntryType::Module(type_index) => { + self.module_type_at(self.state.def(*type_index))?; + Ok(()) + } + ImportSectionEntryType::Instance(type_index) => { + self.instance_type_at(self.state.def(*type_index))?; + Ok(()) + } + } + } + + fn table_type(&self, ty: &TableType) -> Result<()> { + match ty.element_type { + Type::FuncRef => {} + Type::ExternRef => { + if !self.features.reference_types { + return self.create_error("element is not anyfunc"); + } + } + _ => return self.create_error("element is not reference type"), + } + self.limits(&ty.limits)?; + if ty.limits.initial > MAX_WASM_TABLE_ENTRIES as u32 { + return self.create_error("minimum table size is out of bounds"); + } + Ok(()) + } + + fn memory_type(&self, ty: &MemoryType) -> Result<()> { + match ty { + MemoryType::M32 { limits, shared } => { + self.limits(limits)?; + let initial = limits.initial; + if initial as usize > MAX_WASM_MEMORY_PAGES { + return self.create_error("memory size must be at most 65536 pages (4GiB)"); + } + if let Some(maximum) = limits.maximum { + if maximum as usize > MAX_WASM_MEMORY_PAGES { + return self.create_error("memory size must be at most 65536 pages (4GiB)"); + } + } + if *shared { + if !self.features.threads { + return self.create_error("threads must be enabled for shared memories"); + } + if limits.maximum.is_none() { + return self.create_error("shared memory must have maximum size"); + } + } + } + MemoryType::M64 { limits, shared } => { + if !self.features.memory64 { + return self.create_error("memory64 must be enabled for 64-bit memories"); + } + self.limits64(&limits)?; + let initial = limits.initial; + if initial > MAX_WASM_MEMORY64_PAGES { + return self.create_error("memory initial size too large"); + } + if let Some(maximum) = limits.maximum { + if maximum > MAX_WASM_MEMORY64_PAGES { + return self.create_error("memory initial size too large"); + } + } + if *shared { + if !self.features.threads { + return self.create_error("threads must be enabled for shared memories"); + } + if limits.maximum.is_none() { + return self.create_error("shared memory must have maximum size"); + } + } + } + } + Ok(()) + } + + fn global_type(&self, ty: &GlobalType) -> Result<()> { + self.value_type(ty.content_type) + } + + fn limits(&self, limits: &ResizableLimits) -> Result<()> { + if let Some(max) = limits.maximum { + if limits.initial > max { + return self.create_error("size minimum must not be greater than maximum"); + } + } + Ok(()) + } + + fn limits64(&self, limits: &ResizableLimits64) -> Result<()> { + if let Some(max) = limits.maximum { + if limits.initial > max { + return self.create_error("size minimum must not be greater than maximum"); + } + } + Ok(()) + } + + /// Validates [`Payload::ImportSection`](crate::Payload) + pub fn import_section(&mut self, section: &crate::ImportSectionReader<'_>) -> Result<()> { + let order = self.header_order(Order::Import); + self.section(order, section, |me, item| me.import(item)) + } + + fn import(&mut self, entry: Import<'_>) -> Result<()> { + if !self.features.module_linking && entry.field.is_none() { + return self.create_error("module linking proposal is not enabled"); + } + self.import_entry_type(&entry.ty)?; + let (len, max, desc) = match entry.ty { + ImportSectionEntryType::Function(type_index) => { + let def = self.state.def(type_index); + let state = self.state.assert_mut(); + state.func_type_indices.push(def); + (state.func_type_indices.len(), MAX_WASM_FUNCTIONS, "funcs") + } + ImportSectionEntryType::Table(ty) => { + let def = self.state.def(ty); + let state = self.state.assert_mut(); + state.tables.push(def); + (state.tables.len(), self.max_tables(), "tables") + } + ImportSectionEntryType::Memory(ty) => { + let state = self.state.assert_mut(); + state.memories.push(ty); + (state.memories.len(), self.max_memories(), "memories") + } + ImportSectionEntryType::Global(ty) => { + let def = self.state.def(ty); + let state = self.state.assert_mut(); + state.globals.push(def); + (state.globals.len(), MAX_WASM_GLOBALS, "globals") + } + ImportSectionEntryType::Instance(type_idx) => { + let def = self.state.def(InstanceDef::Imported { type_idx }); + let state = self.state.assert_mut(); + state.instance_type_indices.push(def); + ( + state.instance_type_indices.len(), + MAX_WASM_INSTANCES, + "instances", + ) + } + ImportSectionEntryType::Module(type_index) => { + let def = self.state.def(type_index); + let state = self.state.assert_mut(); + state.module_type_indices.push(def); + (state.module_type_indices.len(), MAX_WASM_MODULES, "modules") + } + }; + self.check_max(len, 0, max, desc)?; + + if let Some(ty) = self.expected_type { + let idx = self.expected_import_pos; + self.expected_import_pos += 1; + let module_ty = self.module_type_at(ty)?; + let equal = match module_ty.item.imports.get(idx) { + Some(import) => { + self.imports_equal(self.state.def(entry), module_ty.with(to_import(import))) + } + None => false, + }; + if !equal { + return self.create_error("inline module type does not match declared type"); + } + } + Ok(()) + } + + /// Validates [`Payload::AliasSection`](crate::Payload) + pub fn alias_section(&mut self, section: &crate::AliasSectionReader<'_>) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.section(Order::ModuleLinkingHeader, section, |me, a| me.alias(a)) + } + + fn alias(&mut self, alias: Alias) -> Result<()> { + match alias.instance { + AliasedInstance::Child(instance_idx) => { + let ty = self.get_instance_def(self.state.def(instance_idx))?; + let exports = match ty.item { + InstanceDef::Imported { type_idx } => { + let ty = self.instance_type_at(ty.with(type_idx))?; + ty.map(|t| &t.exports) + } + InstanceDef::Instantiated { module_idx } => { + let ty = self.get_module_type_index(ty.with(module_idx))?; + let ty = self.module_type_at(ty)?; + ty.map(|t| &t.exports) + } + }; + let export = match exports.item.get(alias.index as usize) { + Some(e) => e, + None => { + return self.create_error("aliased export index out of bounds"); + } + }; + match (export.1, alias.kind) { + (ImportSectionEntryType::Function(ty), ExternalKind::Function) => { + let def = exports.with(ty); + self.state.assert_mut().func_type_indices.push(def); + } + (ImportSectionEntryType::Table(ty), ExternalKind::Table) => { + let def = exports.with(ty); + self.state.assert_mut().tables.push(def); + } + (ImportSectionEntryType::Memory(ty), ExternalKind::Memory) => { + self.state.assert_mut().memories.push(ty); + } + (ImportSectionEntryType::Global(ty), ExternalKind::Global) => { + let def = exports.with(ty); + self.state.assert_mut().globals.push(def); + } + (ImportSectionEntryType::Instance(ty), ExternalKind::Instance) => { + let def = exports.with(InstanceDef::Imported { type_idx: ty }); + self.state.assert_mut().instance_type_indices.push(def); + } + (ImportSectionEntryType::Module(ty), ExternalKind::Module) => { + let def = exports.with(ty); + self.state.assert_mut().module_type_indices.push(def); + } + _ => return self.create_error("alias kind mismatch with export kind"), + } + } + AliasedInstance::Parent => { + let idx = match self.state.depth.checked_sub(1) { + None => return self.create_error("no parent module to alias from"), + Some(depth) => Def { + depth, + item: alias.index, + }, + }; + match alias.kind { + ExternalKind::Module => { + let ty = self.get_module_type_index(idx)?; + self.state.assert_mut().module_type_indices.push(ty); + } + ExternalKind::Type => { + // make sure this type actually exists, then push it as + // ourselve aliasing that type. + self.get_type(idx)?; + self.state + .assert_mut() + .types + .push(ValidatedType::Alias(idx)); + } + _ => return self.create_error("only parent types/modules can be aliased"), + } + } + } + + Ok(()) + } + + /// Validates [`Payload::ModuleSection`](crate::Payload) + pub fn module_section(&mut self, section: &crate::ModuleSectionReader<'_>) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.check_max( + self.state.module_type_indices.len(), + section.get_count(), + MAX_WASM_MODULES, + "modules", + )?; + self.expected_modules = Some(section.get_count() + self.expected_modules.unwrap_or(0)); + self.section(Order::ModuleLinkingHeader, section, |me, type_index| { + let type_index = me.state.def(type_index); + me.module_type_at(type_index)?; + me.state.assert_mut().module_type_indices.push(type_index); + Ok(()) + }) + } + + /// Validates [`Payload::InstanceSection`](crate::Payload) + pub fn instance_section(&mut self, section: &crate::InstanceSectionReader<'_>) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.check_max( + self.state.instance_type_indices.len(), + section.get_count(), + MAX_WASM_INSTANCES, + "instances", + )?; + self.section(Order::ModuleLinkingHeader, section, |me, i| me.instance(i)) + } + + fn instance(&mut self, instance: Instance<'_>) -> Result<()> { + // Fetch the type of the instantiated module so we can typecheck all of + // the import items. + let module_idx = instance.module(); + let module_ty = self.get_module_type_index(self.state.def(module_idx))?; + let ty = self.module_type_at(module_ty)?; + + // Make sure the right number of imports are provided + let mut args = instance.args()?; + if args.get_count() as usize != ty.item.imports.len() { + return self.create_error("wrong number of imports provided"); + } + + // Now pairwise match each import against the expected type, making sure + // that the provided type is a subtype of the expected type. + for import_ty in ty.item.imports.iter() { + let (kind, index) = args.read()?; + let index = self.state.def(index); + let actual = match kind { + ExternalKind::Function => self + .get_func_type_index(index)? + .map(ImportSectionEntryType::Function), + ExternalKind::Table => self.get_table(index)?.map(ImportSectionEntryType::Table), + ExternalKind::Memory => self + .state + .def(ImportSectionEntryType::Memory(*self.get_memory(index)?)), + ExternalKind::Global => self.get_global(index)?.map(ImportSectionEntryType::Global), + ExternalKind::Module => self + .get_module_type_index(index)? + .map(ImportSectionEntryType::Module), + ExternalKind::Instance => { + let def = self.get_instance_def(index)?; + match def.item { + InstanceDef::Imported { type_idx } => { + def.with(ImportSectionEntryType::Instance(type_idx)) + } + InstanceDef::Instantiated { module_idx } => { + let expected = match import_ty.2 { + ImportSectionEntryType::Instance(idx) => ty.with(idx), + _ => { + return self + .create_error("wrong kind of item used for instantiate") + } + }; + let expected = self.instance_type_at(expected)?; + let module_idx = def.with(module_idx); + let actual = self.get_module_type_index(module_idx)?; + let actual = self.module_type_at(actual)?; + self.check_export_sets_match( + expected.map(|m| &*m.exports), + actual.map(|m| &*m.exports), + )?; + continue; + } + } + } + ExternalKind::Type => return self.create_error("cannot export types"), + }; + let item = actual.item; + self.check_imports_match(ty.with(&import_ty.2), actual.map(|_| &item))?; + } + args.ensure_end()?; + + let def = self.state.def(InstanceDef::Instantiated { module_idx }); + self.state.assert_mut().instance_type_indices.push(def); + Ok(()) + } + + // Note that this function is basically implementing + // https://webassembly.github.io/spec/core/exec/modules.html#import-matching + fn check_imports_match( + &self, + expected: Def<&ImportSectionEntryType>, + actual: Def<&ImportSectionEntryType>, + ) -> Result<()> { + macro_rules! limits_match { + ($expected:expr, $actual:expr) => {{ + let expected = $expected; + let actual = $actual; + actual.initial >= expected.initial + && match expected.maximum { + Some(expected_max) => match actual.maximum { + Some(actual_max) => actual_max <= expected_max, + None => false, + }, + None => true, + } + }}; + } + match (expected.item, actual.item) { + ( + ImportSectionEntryType::Function(expected_idx), + ImportSectionEntryType::Function(actual_idx), + ) => { + let expected = self.func_type_at(expected.map(|_| *expected_idx))?; + let actual = self.func_type_at(actual.map(|_| *actual_idx))?; + if actual.item == expected.item { + return Ok(()); + } + self.create_error("function provided for instantiation has wrong type") + } + (ImportSectionEntryType::Table(expected), ImportSectionEntryType::Table(actual)) => { + if expected.element_type == actual.element_type + && limits_match!(&expected.limits, &actual.limits) + { + return Ok(()); + } + self.create_error("table provided for instantiation has wrong type") + } + (ImportSectionEntryType::Memory(expected), ImportSectionEntryType::Memory(actual)) => { + match (expected, actual) { + ( + MemoryType::M32 { + limits: a, + shared: ash, + }, + MemoryType::M32 { + limits: b, + shared: bsh, + }, + ) => { + if limits_match!(a, b) && ash == bsh { + return Ok(()); + } + } + ( + MemoryType::M64 { + limits: a, + shared: ash, + }, + MemoryType::M64 { + limits: b, + shared: bsh, + }, + ) => { + if limits_match!(a, b) && ash == bsh { + return Ok(()); + } + } + _ => {} + } + self.create_error("memory provided for instantiation has wrong type") + } + (ImportSectionEntryType::Global(expected), ImportSectionEntryType::Global(actual)) => { + if expected == actual { + return Ok(()); + } + self.create_error("global provided for instantiation has wrong type") + } + ( + ImportSectionEntryType::Instance(expected_idx), + ImportSectionEntryType::Instance(actual_idx), + ) => { + let expected = self.instance_type_at(expected.map(|_| *expected_idx))?; + let actual = self.instance_type_at(actual.map(|_| *actual_idx))?; + self.check_export_sets_match( + expected.map(|i| &*i.exports), + actual.map(|i| &*i.exports), + )?; + Ok(()) + } + ( + ImportSectionEntryType::Module(expected_idx), + ImportSectionEntryType::Module(actual_idx), + ) => { + let expected = self.module_type_at(expected.map(|_| *expected_idx))?; + let actual = self.module_type_at(actual.map(|_| *actual_idx))?; + if expected.item.imports.len() != actual.item.imports.len() { + return self.create_error("mismatched number of module imports"); + } + for (a, b) in expected.item.imports.iter().zip(actual.item.imports.iter()) { + self.check_imports_match(expected.map(|_| &a.2), actual.map(|_| &b.2))?; + } + self.check_export_sets_match( + expected.map(|i| &*i.exports), + actual.map(|i| &*i.exports), + )?; + Ok(()) + } + _ => self.create_error("wrong kind of item used for instantiate"), + } + } + + fn check_export_sets_match( + &self, + expected: Def<&[(String, ImportSectionEntryType)]>, + actual: Def<&[(String, ImportSectionEntryType)]>, + ) -> Result<()> { + let name_to_idx = actual + .item + .iter() + .enumerate() + .map(|(i, e)| (&e.0, i)) + .collect::<HashMap<_, _>>(); + for expected_export in expected.item { + let idx = match name_to_idx.get(&expected_export.0) { + Some(i) => *i, + None => { + return self.create_error(&format!("no export named `{}`", expected_export.0)) + } + }; + self.check_imports_match( + expected.map(|_| &expected_export.1), + actual.map(|_| &actual.item[idx].1), + )?; + } + Ok(()) + } + + /// Validates [`Payload::FunctionSection`](crate::Payload) + pub fn function_section(&mut self, section: &crate::FunctionSectionReader<'_>) -> Result<()> { + self.expected_code_bodies = Some(section.get_count()); + self.check_max( + self.state.func_type_indices.len(), + section.get_count(), + MAX_WASM_FUNCTIONS, + "funcs", + )?; + // Assert that each type index is indeed a function type, and otherwise + // just push it for handling later. + self.section(Order::Function, section, |me, i| { + let type_index = me.state.def(i); + me.func_type_at(type_index)?; + me.state.assert_mut().func_type_indices.push(type_index); + Ok(()) + }) + } + + fn max_tables(&self) -> usize { + if self.features.reference_types || self.features.module_linking { + MAX_WASM_TABLES + } else { + 1 + } + } + + /// Validates [`Payload::TableSection`](crate::Payload) + pub fn table_section(&mut self, section: &crate::TableSectionReader<'_>) -> Result<()> { + self.check_max( + self.state.tables.len(), + section.get_count(), + self.max_tables(), + "tables", + )?; + self.section(Order::Table, section, |me, ty| { + me.table_type(&ty)?; + let def = me.state.def(ty); + me.state.assert_mut().tables.push(def); + Ok(()) + }) + } + + fn max_memories(&self) -> usize { + if self.features.multi_memory { + MAX_WASM_MEMORIES + } else { + 1 + } + } + + pub fn memory_section(&mut self, section: &crate::MemorySectionReader<'_>) -> Result<()> { + self.check_max( + self.state.memories.len(), + section.get_count(), + self.max_memories(), + "memories", + )?; + self.section(Order::Memory, section, |me, ty| { + me.memory_type(&ty)?; + me.state.assert_mut().memories.push(ty); + Ok(()) + }) + } + + /// Validates [`Payload::GlobalSection`](crate::Payload) + pub fn global_section(&mut self, section: &crate::GlobalSectionReader<'_>) -> Result<()> { + self.check_max( + self.state.globals.len(), + section.get_count(), + MAX_WASM_GLOBALS, + "globals", + )?; + self.section(Order::Global, section, |me, g| { + me.global_type(&g.ty)?; + me.init_expr(&g.init_expr, g.ty.content_type, false)?; + let def = me.state.def(g.ty); + me.state.assert_mut().globals.push(def); + Ok(()) + }) + } + + fn init_expr(&mut self, expr: &InitExpr<'_>, expected_ty: Type, allow32: bool) -> Result<()> { + let mut ops = expr.get_operators_reader().into_iter_with_offsets(); + let (op, offset) = match ops.next() { + Some(Err(e)) => return Err(e), + Some(Ok(pair)) => pair, + None => return self.create_error("type mismatch: init_expr is empty"), + }; + self.offset = offset; + let ty = match op { + Operator::I32Const { .. } => Type::I32, + Operator::I64Const { .. } => Type::I64, + Operator::F32Const { .. } => Type::F32, + Operator::F64Const { .. } => Type::F64, + Operator::RefNull { ty } => ty, + Operator::V128Const { .. } => Type::V128, + Operator::GlobalGet { global_index } => { + self.get_global(self.state.def(global_index))? + .item + .content_type + } + Operator::RefFunc { function_index } => { + self.get_func_type_index(self.state.def(function_index))?; + self.state + .assert_mut() + .function_references + .insert(function_index); + Type::FuncRef + } + Operator::End => return self.create_error("type mismatch: init_expr is empty"), + _ => { + return self + .create_error("constant expression required: invalid init_expr operator") + } + }; + if ty != expected_ty { + if !allow32 || ty != Type::I32 { + return self.create_error("type mismatch: invalid init_expr type"); + } + } + + // Make sure the next instruction is an `end` + match ops.next() { + Some(Err(e)) => return Err(e), + Some(Ok((Operator::End, _))) => {} + Some(Ok(_)) => { + return self + .create_error("constant expression required: type mismatch: only one init_expr operator is expected") + } + None => return self.create_error("type mismatch: init_expr is not terminated"), + } + + // ... and verify we're done after that + match ops.next() { + Some(Err(e)) => Err(e), + Some(Ok(_)) => { + self.create_error("constant expression required: invalid init_expr operator") + } + None => Ok(()), + } + } + + /// Validates [`Payload::ExportSection`](crate::Payload) + pub fn export_section(&mut self, section: &crate::ExportSectionReader<'_>) -> Result<()> { + let mut exported_names = HashSet::new(); + self.section(Order::Export, section, |me, e| { + if !exported_names.insert(e.field.to_string()) { + return me.create_error("duplicate export name"); + } + if let ExternalKind::Type = e.kind { + return me.create_error("cannot export types"); + } + me.check_external_kind("exported", e.kind, e.index)?; + if !me.export_is_expected(e)? { + return me.create_error("inline module type does not match declared type"); + } + Ok(()) + }) + } + + fn check_external_kind(&mut self, desc: &str, kind: ExternalKind, index: u32) -> Result<()> { + let (ty, total) = match kind { + ExternalKind::Function => ("function", self.state.func_type_indices.len()), + ExternalKind::Table => ("table", self.state.tables.len()), + ExternalKind::Memory => ("memory", self.state.memories.len()), + ExternalKind::Global => ("global", self.state.globals.len()), + ExternalKind::Module => ("module", self.state.module_type_indices.len()), + ExternalKind::Instance => ("instance", self.state.instance_type_indices.len()), + ExternalKind::Type => return self.create_error("cannot export types"), + }; + if index as usize >= total { + return self.create_error(&format!( + "unknown {ty} {index}: {desc} {ty} index out of bounds", + desc = desc, + index = index, + ty = ty, + )); + } + if let ExternalKind::Function = kind { + self.state.assert_mut().function_references.insert(index); + } + Ok(()) + } + + fn export_is_expected(&mut self, actual: Export<'_>) -> Result<bool> { + let expected_ty = match self.expected_type { + Some(ty) => ty, + None => return Ok(true), + }; + let idx = self.expected_export_pos; + self.expected_export_pos += 1; + let module_ty = self.module_type_at(expected_ty)?; + let expected = match module_ty.item.exports.get(idx) { + Some(expected) => module_ty.with(to_export(expected)), + None => return Ok(false), + }; + let index = self.state.def(actual.index); + let ty = match actual.kind { + ExternalKind::Function => self + .get_func_type_index(index)? + .map(ImportSectionEntryType::Function), + ExternalKind::Table => self.get_table(index)?.map(ImportSectionEntryType::Table), + ExternalKind::Memory => { + let mem = *self.get_memory(index)?; + let ty = ImportSectionEntryType::Memory(mem); + self.state.def(ty) + } + ExternalKind::Global => self.get_global(index)?.map(ImportSectionEntryType::Global), + ExternalKind::Module => self + .get_module_type_index(index)? + .map(ImportSectionEntryType::Module), + ExternalKind::Instance => { + let def = self.get_instance_def(index)?; + match def.item { + InstanceDef::Imported { type_idx } => { + def.with(ImportSectionEntryType::Instance(type_idx)) + } + InstanceDef::Instantiated { module_idx } => { + let a = self.get_module_type_index(def.with(module_idx))?; + let a = self.module_type_at(a)?; + let b = match expected.item.ty { + ImportSectionEntryType::Instance(idx) => { + self.instance_type_at(expected.with(idx))? + } + _ => return Ok(false), + }; + return Ok(actual.field == expected.item.name + && a.item.exports.len() == b.item.exports.len() + && a.item + .exports + .iter() + .map(to_export) + .zip(b.item.exports.iter().map(to_export)) + .all(|(ae, be)| self.exports_equal(a.with(ae), b.with(be)))); + } + } + } + ExternalKind::Type => unreachable!(), // already validated to not exist + }; + let actual = ty.map(|ty| ExportType { + name: actual.field, + ty, + }); + Ok(self.exports_equal(actual, expected)) + } + + fn imports_equal(&self, a: Def<Import<'_>>, b: Def<Import<'_>>) -> bool { + a.item.module == b.item.module + && a.item.field == b.item.field + && self.import_ty_equal(a.with(&a.item.ty), b.with(&b.item.ty)) + } + + fn exports_equal(&self, a: Def<ExportType<'_>>, b: Def<ExportType<'_>>) -> bool { + a.item.name == b.item.name && self.import_ty_equal(a.with(&a.item.ty), b.with(&b.item.ty)) + } + + fn import_ty_equal( + &self, + a: Def<&ImportSectionEntryType>, + b: Def<&ImportSectionEntryType>, + ) -> bool { + match (a.item, b.item) { + (ImportSectionEntryType::Function(ai), ImportSectionEntryType::Function(bi)) => { + self.func_type_at(a.with(*ai)).unwrap().item + == self.func_type_at(b.with(*bi)).unwrap().item + } + (ImportSectionEntryType::Table(a), ImportSectionEntryType::Table(b)) => a == b, + (ImportSectionEntryType::Memory(a), ImportSectionEntryType::Memory(b)) => a == b, + (ImportSectionEntryType::Global(a), ImportSectionEntryType::Global(b)) => a == b, + (ImportSectionEntryType::Instance(ai), ImportSectionEntryType::Instance(bi)) => { + let a = self.instance_type_at(a.with(*ai)).unwrap(); + let b = self.instance_type_at(b.with(*bi)).unwrap(); + a.item.exports.len() == b.item.exports.len() + && a.item + .exports + .iter() + .map(to_export) + .zip(b.item.exports.iter().map(to_export)) + .all(|(ae, be)| self.exports_equal(a.with(ae), b.with(be))) + } + (ImportSectionEntryType::Module(ai), ImportSectionEntryType::Module(bi)) => { + let a = self.module_type_at(a.with(*ai)).unwrap(); + let b = self.module_type_at(b.with(*bi)).unwrap(); + a.item.imports.len() == b.item.imports.len() + && a.item + .imports + .iter() + .map(to_import) + .zip(b.item.imports.iter().map(to_import)) + .all(|(ai, bi)| self.imports_equal(a.with(ai), b.with(bi))) + && a.item.exports.len() == b.item.exports.len() + && a.item + .exports + .iter() + .map(to_export) + .zip(b.item.exports.iter().map(to_export)) + .all(|(ae, be)| self.exports_equal(a.with(ae), b.with(be))) + } + _ => false, + } + } + + /// Validates [`Payload::StartSection`](crate::Payload) + pub fn start_section(&mut self, func: u32, range: &Range) -> Result<()> { + self.offset = range.start; + self.update_order(Order::Start)?; + let ty = self.get_func_type_index(self.state.def(func))?; + let ty = self.func_type_at(ty)?; + if !ty.item.params.is_empty() || !ty.item.returns.is_empty() { + return self.create_error("invalid start function type"); + } + Ok(()) + } + + /// Validates [`Payload::ElementSection`](crate::Payload) + pub fn element_section(&mut self, section: &crate::ElementSectionReader<'_>) -> Result<()> { + self.section(Order::Element, section, |me, e| { + match e.ty { + Type::FuncRef => {} + Type::ExternRef if me.features.reference_types => {} + Type::ExternRef => { + return me + .create_error("reference types must be enabled for anyref elem segment"); + } + _ => return me.create_error("invalid reference type"), + } + match e.kind { + ElementKind::Active { + table_index, + init_expr, + } => { + let table = me.get_table(me.state.def(table_index))?; + if e.ty != table.item.element_type { + return me.create_error("element_type != table type"); + } + me.init_expr(&init_expr, Type::I32, false)?; + } + ElementKind::Passive | ElementKind::Declared => { + if !me.features.bulk_memory { + return me.create_error("reference types must be enabled"); + } + } + } + let mut items = e.items.get_items_reader()?; + if items.get_count() > MAX_WASM_TABLE_ENTRIES as u32 { + return me.create_error("num_elements is out of bounds"); + } + for _ in 0..items.get_count() { + me.offset = items.original_position(); + match items.read()? { + ElementItem::Null(ty) => { + if ty != e.ty { + return me.create_error( + "type mismatch: null type doesn't match element type", + ); + } + } + ElementItem::Func(f) => { + if e.ty != Type::FuncRef { + return me + .create_error("type mismatch: segment does not have funcref type"); + } + me.get_func_type_index(me.state.def(f))?; + me.state.assert_mut().function_references.insert(f); + } + } + } + + me.state.assert_mut().element_types.push(e.ty); + Ok(()) + }) + } + + /// Validates [`Payload::DataCountSection`](crate::Payload) + pub fn data_count_section(&mut self, count: u32, range: &Range) -> Result<()> { + self.offset = range.start; + self.update_order(Order::DataCount)?; + self.state.assert_mut().data_count = Some(count); + if count > MAX_WASM_DATA_SEGMENTS as u32 { + return self.create_error("data count section specifies too many data segments"); + } + Ok(()) + } + + /// Validates [`Payload::ModuleCodeSectionStart`](crate::Payload) + pub fn module_code_section_start(&mut self, count: u32, range: &Range) -> Result<()> { + if !self.features.module_linking { + return self.create_error("module linking proposal not enabled"); + } + self.offset = range.start; + self.update_order(Order::ModuleCode)?; + match self.expected_modules.take() { + Some(n) if n == count => {} + Some(_) => { + return self + .create_error("module and module code section have inconsistent lengths"); + } + None if count == 0 => {} + None => return self.create_error("module code section without module section"), + } + self.module_code_section_index = self.state.module_type_indices.len() - (count as usize); + Ok(()) + } + + /// Validates [`Payload::ModuleCodeSectionEntry`](crate::Payload). + /// + /// Note that this does not actually perform any validation itself. The + /// `ModuleCodeSectionEntry` payload is associated with a sub-parser for the + /// sub-module, and it's expected that the returned [`Validator`] will be + /// paired with the [`Parser`] otherwise used with the module. + /// + /// Note that the returned [`Validator`] should be used for the nested + /// module. It will correctly work with parent aliases as well as ensure the + /// type of the inline module matches the declared type. Using + /// [`Validator::new`] will result in incorrect validation. + pub fn module_code_section_entry<'a>(&mut self) -> Validator { + let mut ret = Validator::default(); + ret.features = self.features.clone(); + ret.expected_type = Some(self.state.module_type_indices[self.module_code_section_index]); + self.module_code_section_index += 1; + let state = ret.state.assert_mut(); + state.parent = Some(self.state.arc().clone()); + state.depth = self.state.depth + 1; + return ret; + } + + /// Validates [`Payload::CodeSectionStart`](crate::Payload). + pub fn code_section_start(&mut self, count: u32, range: &Range) -> Result<()> { + self.offset = range.start; + self.update_order(Order::Code)?; + match self.expected_code_bodies.take() { + Some(n) if n == count => {} + Some(_) => { + return self.create_error("function and code section have inconsistent lengths"); + } + // empty code sections are allowed even if the function section is + // missing + None if count == 0 => {} + None => return self.create_error("code section without function section"), + } + self.code_section_index = self.state.func_type_indices.len() - (count as usize); + Ok(()) + } + + /// Validates [`Payload::CodeSectionEntry`](crate::Payload). + /// + /// This function will prepare a [`FuncValidator`] which can be used to + /// validate the function. The function body provided will be parsed only + /// enough to create the function validation context. After this the + /// [`OperatorsReader`](crate::readers::OperatorsReader) returned can be used to read the + /// opcodes of the function as well as feed information into the validator. + /// + /// Note that the returned [`FuncValidator`] is "connected" to this + /// [`Validator`] in that it uses the internal context of this validator for + /// validating the function. The [`FuncValidator`] can be sent to + /// another thread, for example, to offload actual processing of functions + /// elsewhere. + pub fn code_section_entry(&mut self) -> Result<FuncValidator<ValidatorResources>> { + let ty_index = self.state.func_type_indices[self.code_section_index]; + self.code_section_index += 1; + let resources = ValidatorResources(self.state.arc().clone()); + Ok(FuncValidator::new(ty_index.item, 0, resources, &self.features).unwrap()) + } + + /// Validates [`Payload::DataSection`](crate::Payload). + pub fn data_section(&mut self, section: &crate::DataSectionReader<'_>) -> Result<()> { + self.data_found = section.get_count(); + self.check_max(0, section.get_count(), MAX_WASM_DATA_SEGMENTS, "segments")?; + self.section(Order::Data, section, |me, d| { + match d.kind { + DataKind::Passive => {} + DataKind::Active { + memory_index, + init_expr, + } => { + let ty = me.get_memory(me.state.def(memory_index))?.index_type(); + let allow32 = ty == Type::I64; + me.init_expr(&init_expr, ty, allow32)?; + } + } + Ok(()) + }) + } + + /// Validates [`Payload::UnknownSection`](crate::Payload). + /// + /// Currently always returns an error. + pub fn unknown_section(&mut self, id: u8, range: &Range) -> Result<()> { + self.offset = range.start; + self.create_error(format!("invalid section code: {}", id)) + } + + /// Validates [`Payload::End`](crate::Payload). + pub fn end(&mut self) -> Result<()> { + if let Some(data_count) = self.state.data_count { + if data_count != self.data_found { + return self.create_error("data count section and passive data mismatch"); + } + } + if let Some(n) = self.expected_code_bodies.take() { + if n > 0 { + return self.create_error("function and code sections have inconsistent lengths"); + } + } + if let Some(n) = self.expected_modules.take() { + if n > 0 { + return self + .create_error("module and module code sections have inconsistent lengths"); + } + } + if let Some(t) = self.expected_type { + let ty = self.module_type_at(t)?; + if self.expected_import_pos != ty.item.imports.len() + || self.expected_export_pos != ty.item.exports.len() + { + return self.create_error("inline module type does not match declared type"); + } + } + Ok(()) + } +} + +impl WasmFeatures { + pub(crate) fn check_value_type(&self, ty: Type) -> Result<(), &'static str> { + match ty { + Type::I32 | Type::I64 | Type::F32 | Type::F64 => Ok(()), + Type::FuncRef | Type::ExternRef => { + if self.reference_types { + Ok(()) + } else { + Err("reference types support is not enabled") + } + } + Type::V128 => { + if self.simd { + Ok(()) + } else { + Err("SIMD support is not enabled") + } + } + _ => Err("invalid value type"), + } + } +} + +impl ModuleState { + fn def<T>(&self, item: T) -> Def<T> { + Def { + depth: self.depth, + item, + } + } + + fn get<'me, T>( + &'me self, + idx: Def<u32>, + get_list: impl FnOnce(&'me ModuleState) -> &'me [T], + ) -> Option<&'me T> { + let mut cur = self; + for _ in 0..(self.depth - idx.depth) { + cur = cur.parent.as_ref().unwrap(); + } + get_list(cur).get(idx.item as usize) + } + + fn get_type<'me>(&'me self, mut idx: Def<u32>) -> Option<Def<&'me TypeDef>> { + loop { + let def = self.get(idx, |v| &v.types)?; + match def { + ValidatedType::Def(item) => break Some(idx.with(item)), + ValidatedType::Alias(other) => idx = *other, + } + } + } + + fn get_table<'me>(&'me self, idx: Def<u32>) -> Option<&'me Def<TableType>> { + self.get(idx, |v| &v.tables) + } + + fn get_memory<'me>(&'me self, idx: Def<u32>) -> Option<&'me MemoryType> { + self.get(idx, |v| &v.memories) + } + + fn get_global<'me>(&'me self, idx: Def<u32>) -> Option<&'me Def<GlobalType>> { + self.get(idx, |v| &v.globals) + } + + fn get_func_type_index<'me>(&'me self, idx: Def<u32>) -> Option<Def<u32>> { + Some(*self.get(idx, |v| &v.func_type_indices)?) + } + + fn get_module_type_index<'me>(&'me self, idx: Def<u32>) -> Option<Def<u32>> { + Some(*self.get(idx, |v| &v.module_type_indices)?) + } + + fn get_instance_def<'me>(&'me self, idx: Def<u32>) -> Option<&'me Def<InstanceDef>> { + self.get(idx, |v| &v.instance_type_indices) + } +} + +#[derive(Copy, Clone)] +struct Def<T> { + depth: usize, + item: T, +} + +impl<T> Def<T> { + fn map<U>(self, map: impl FnOnce(T) -> U) -> Def<U> { + Def { + depth: self.depth, + item: map(self.item), + } + } + + fn with<U>(&self, item: U) -> Def<U> { + Def { + depth: self.depth, + item: item, + } + } +} + +/// The implementation of [`WasmModuleResources`] used by [`Validator`]. +pub struct ValidatorResources(Arc<ModuleState>); + +impl WasmModuleResources for ValidatorResources { + type FuncType = crate::FuncType; + + fn table_at(&self, at: u32) -> Option<TableType> { + self.0.get_table(self.0.def(at)).map(|t| t.item) + } + + fn memory_at(&self, at: u32) -> Option<MemoryType> { + self.0.get_memory(self.0.def(at)).copied() + } + + fn global_at(&self, at: u32) -> Option<GlobalType> { + self.0.get_global(self.0.def(at)).map(|t| t.item) + } + + fn func_type_at(&self, at: u32) -> Option<&Self::FuncType> { + match self.0.get_type(self.0.def(at))?.item { + TypeDef::Func(f) => Some(f), + _ => None, + } + } + + fn type_of_function(&self, at: u32) -> Option<&Self::FuncType> { + let ty = self.0.get_func_type_index(self.0.def(at))?; + match self.0.get_type(ty)?.item { + TypeDef::Func(f) => Some(f), + _ => None, + } + } + + fn element_type_at(&self, at: u32) -> Option<Type> { + self.0.element_types.get(at as usize).cloned() + } + + fn element_count(&self) -> u32 { + self.0.element_types.len() as u32 + } + + fn data_count(&self) -> u32 { + self.0.data_count.unwrap_or(0) + } + + fn is_function_referenced(&self, idx: u32) -> bool { + self.0.function_references.contains(&idx) + } +} + +mod arc { + use std::ops::Deref; + use std::sync::Arc; + + pub struct MaybeOwned<T> { + owned: bool, + arc: Arc<T>, + } + + impl<T> MaybeOwned<T> { + pub fn as_mut(&mut self) -> Option<&mut T> { + if !self.owned { + return None; + } + debug_assert!(Arc::get_mut(&mut self.arc).is_some()); + Some(unsafe { &mut *(&*self.arc as *const T as *mut T) }) + } + + pub fn assert_mut(&mut self) -> &mut T { + self.as_mut().unwrap() + } + + pub fn arc(&mut self) -> &Arc<T> { + self.owned = false; + &self.arc + } + } + + impl<T: Default> Default for MaybeOwned<T> { + fn default() -> MaybeOwned<T> { + MaybeOwned { + owned: true, + arc: Arc::default(), + } + } + } + + impl<T> Deref for MaybeOwned<T> { + type Target = T; + + fn deref(&self) -> &T { + &self.arc + } + } +} |