/* 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` 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, /// 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, code_section_index: usize, /// Similar to code bodies above, but for module bodies instead. expected_modules: Option, 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>, expected_import_pos: usize, expected_export_pos: usize, } #[derive(Default)] struct ModuleState { depth: usize, types: Vec, tables: Vec>, memories: Vec, globals: Vec>, element_types: Vec, data_count: Option, func_type_indices: Vec>, module_type_indices: Vec>, instance_type_indices: Vec>, function_references: HashSet, parent: Option>, } /// 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), } enum TypeDef { Func(FuncType), Module(ModuleType), Instance(InstanceType), } struct ModuleType { imports: Vec<(String, Option, ImportSectionEntryType)>, exports: Vec<(String, ImportSectionEntryType)>, } struct InstanceType { exports: Vec<(String, ImportSectionEntryType)>, } fn to_import(item: &(String, Option, 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, 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> { 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(&self, msg: impl Into) -> Result { 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) -> Result> { 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) -> Result<&'me Def> { 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) -> 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) -> Result<&'me Def> { 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) -> Result> { 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) -> Result> { 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) -> Result<&'me Def> { 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) -> Result> { 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) -> Result> { 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) -> Result> { 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( &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::>()?; 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::>()?; 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::>()?; 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::>(); 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 { 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>, b: Def>) -> 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>, b: Def>) -> 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> { 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(&self, item: T) -> Def { Def { depth: self.depth, item, } } fn get<'me, T>( &'me self, idx: Def, 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) -> Option> { 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) -> Option<&'me Def> { self.get(idx, |v| &v.tables) } fn get_memory<'me>(&'me self, idx: Def) -> Option<&'me MemoryType> { self.get(idx, |v| &v.memories) } fn get_global<'me>(&'me self, idx: Def) -> Option<&'me Def> { self.get(idx, |v| &v.globals) } fn get_func_type_index<'me>(&'me self, idx: Def) -> Option> { Some(*self.get(idx, |v| &v.func_type_indices)?) } fn get_module_type_index<'me>(&'me self, idx: Def) -> Option> { Some(*self.get(idx, |v| &v.module_type_indices)?) } fn get_instance_def<'me>(&'me self, idx: Def) -> Option<&'me Def> { self.get(idx, |v| &v.instance_type_indices) } } #[derive(Copy, Clone)] struct Def { depth: usize, item: T, } impl Def { fn map(self, map: impl FnOnce(T) -> U) -> Def { Def { depth: self.depth, item: map(self.item), } } fn with(&self, item: U) -> Def { Def { depth: self.depth, item: item, } } } /// The implementation of [`WasmModuleResources`] used by [`Validator`]. pub struct ValidatorResources(Arc); impl WasmModuleResources for ValidatorResources { type FuncType = crate::FuncType; fn table_at(&self, at: u32) -> Option { self.0.get_table(self.0.def(at)).map(|t| t.item) } fn memory_at(&self, at: u32) -> Option { self.0.get_memory(self.0.def(at)).copied() } fn global_at(&self, at: u32) -> Option { 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 { 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 { owned: bool, arc: Arc, } impl MaybeOwned { 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 { self.owned = false; &self.arc } } impl Default for MaybeOwned { fn default() -> MaybeOwned { MaybeOwned { owned: true, arc: Arc::default(), } } } impl Deref for MaybeOwned { type Target = T; fn deref(&self) -> &T { &self.arc } } }