diff --git a/src/core/error.rs b/src/core/error.rs index 37b7b41406..ce594a15d7 100644 --- a/src/core/error.rs +++ b/src/core/error.rs @@ -153,6 +153,18 @@ pub enum ValidationError { /// The mode of an element was invalid. Only values in the range 0..=7 are /// allowed. InvalidElementMode(u32), + /// The module contains too many functions, i.e. imported or locally-defined + /// functions. The maximum number of functions is [`u32::MAX`]. + TooManyFunctions, + /// The module contains too many tables, i.e. imported or locally-defined + /// tables. The maximum number of tables is [`u32::MAX`]. + TooManyTables, + /// The module contains too many memories, i.e. imported or locally-defined + /// memories. The maximum number of memories is [`u32::MAX`]. + TooManyMemories, + /// The module contains too many globals, i.e. imported or locally-defined + /// globals. The maximum number of memories is [`u32::MAX`]. + TooManyGlobals, } impl core::error::Error for ValidationError {} @@ -226,6 +238,10 @@ impl Display for ValidationError { ValidationError::MissingDataCountSection => f.write_str("Some instructions could not be validated because the data count section is missing"), ValidationError::InvalidDataSegmentMode(mode) => write!(f, "The mode of a data segment was invalid (only 0..=2 is allowed): {mode}"), ValidationError::InvalidElementMode(mode) => write!(f, "The mode of an element was invalid (only 0..=7 is allowed): {mode}"), + ValidationError::TooManyFunctions => f.write_str("The module contains too many functions. The maximum number of functions (either imported or locally-defined) is 2^32 - 1"), + ValidationError::TooManyTables => f.write_str("The module contains too many tables. The maximum number of tables (either imported or locally-defined) is 2^32 - 1"), + ValidationError::TooManyMemories => f.write_str("The module contains too many memories. The maximum number of memories (either imported or locally-defined) is 2^32 - 1"), + ValidationError::TooManyGlobals => f.write_str("The module contains too many globals. The maximum number of globals (either imported or locally-defined) is 2^32 - 1"), } } } diff --git a/src/core/indices.rs b/src/core/indices.rs index 13e8c4c9fb..75cd1015b7 100644 --- a/src/core/indices.rs +++ b/src/core/indices.rs @@ -24,5 +24,4 @@ pub type GlobalIdx = usize; pub type ElemIdx = usize; pub type DataIdx = usize; pub type LocalIdx = usize; -#[allow(dead_code)] pub type LabelIdx = usize; diff --git a/src/core/reader/mod.rs b/src/core/reader/mod.rs index 8e1583b841..25d4edfc89 100644 --- a/src/core/reader/mod.rs +++ b/src/core/reader/mod.rs @@ -137,7 +137,6 @@ impl<'a> WasmReader<'a> { /// more than 0 further bytes would panick. However, it can not move the [`pc`](Self::pc) any /// further than that, instead an error is returned. For further information, refer to the /// [field documentation of `pc`] (WasmReader::pc). - #[allow(dead_code)] pub fn skip(&mut self, num_bytes: usize) -> Result<(), ValidationError> { if num_bytes > self.full_wasm_binary.len() - self.pc { return Err(ValidationError::Eof); @@ -155,7 +154,6 @@ impl<'a> WasmReader<'a> { /// /// The provided closure will be called with `&mut self` and its result will be returned. /// However if the closure returns `Err(_)`, `self` will be reset as if the closure was never called. - #[allow(dead_code)] pub fn handle_transaction( &mut self, f: impl FnOnce(&mut WasmReader<'a>) -> Result, diff --git a/src/core/reader/types/export.rs b/src/core/reader/types/export.rs index 86c30e7b09..72ba045c62 100644 --- a/src/core/reader/types/export.rs +++ b/src/core/reader/types/export.rs @@ -1,6 +1,3 @@ -use alloc::borrow::ToOwned; -use alloc::string::String; - use crate::core::indices::{FuncIdx, GlobalIdx, MemIdx, TableIdx}; use crate::core::reader::types::import::ImportDesc; use crate::core::reader::WasmReader; @@ -9,33 +6,25 @@ use crate::{ValidationError, ValidationInfo}; use super::ExternType; #[derive(Debug, Clone)] -pub struct Export { - #[allow(dead_code)] - pub name: String, - #[allow(dead_code)] +pub struct Export<'wasm> { + pub name: &'wasm str, pub desc: ExportDesc, } -impl Export { - pub fn read(wasm: &mut WasmReader) -> Result { - let name = wasm.read_name()?.to_owned(); +impl<'wasm> Export<'wasm> { + pub fn read(wasm: &mut WasmReader<'wasm>) -> Result { + let name = wasm.read_name()?; let desc = ExportDesc::read(wasm)?; Ok(Export { name, desc }) } } #[derive(Debug, Clone)] -#[allow(clippy::all)] -// TODO: change enum labels from FuncIdx -> Func pub enum ExportDesc { - #[allow(warnings)] - FuncIdx(FuncIdx), - #[allow(warnings)] - TableIdx(TableIdx), - #[allow(warnings)] - MemIdx(MemIdx), - #[allow(warnings)] - GlobalIdx(GlobalIdx), + Func(FuncIdx), + Table(TableIdx), + Mem(MemIdx), + Global(GlobalIdx), } impl ExportDesc { @@ -44,11 +33,12 @@ impl ExportDesc { /// /// Note: This method may panic if `self` does not come from the given [`ValidationInfo`]. /// + #[allow(unused)] // reason = "this function is analogous to ImportDesc::extern_type, however it is not yet clear if it is needed in the future" pub fn extern_type(&self, validation_info: &ValidationInfo) -> ExternType { // TODO clean up logic for checking if an exported definition is an // import match self { - ExportDesc::FuncIdx(func_idx) => { + ExportDesc::Func(func_idx) => { let type_idx = match func_idx .checked_sub(validation_info.imports_length.imported_functions) { @@ -70,7 +60,7 @@ impl ExportDesc { // TODO ugly clone that should disappear when types are directly parsed from bytecode instead of vector copies ExternType::Func(func_type.clone()) } - ExportDesc::TableIdx(table_idx) => { + ExportDesc::Table(table_idx) => { let table_type = match table_idx .checked_sub(validation_info.imports_length.imported_tables) { @@ -87,7 +77,7 @@ impl ExportDesc { }; ExternType::Table(table_type) } - ExportDesc::MemIdx(mem_idx) => { + ExportDesc::Mem(mem_idx) => { let mem_type = match mem_idx .checked_sub(validation_info.imports_length.imported_memories) { @@ -104,7 +94,7 @@ impl ExportDesc { }; ExternType::Mem(mem_type) } - ExportDesc::GlobalIdx(global_idx) => { + ExportDesc::Global(global_idx) => { let global_type = match global_idx.checked_sub(validation_info.imports_length.imported_globals) { Some(local_global_idx) => { @@ -125,34 +115,6 @@ impl ExportDesc { } } } - - pub fn get_function_idx(&self) -> Option { - match self { - ExportDesc::FuncIdx(func_idx) => Some(*func_idx), - _ => None, - } - } - - pub fn get_global_idx(&self) -> Option { - match self { - ExportDesc::GlobalIdx(global_idx) => Some(*global_idx), - _ => None, - } - } - - pub fn get_memory_idx(&self) -> Option { - match self { - ExportDesc::MemIdx(mem_idx) => Some(*mem_idx), - _ => None, - } - } - - pub fn get_table_idx(&self) -> Option { - match self { - ExportDesc::TableIdx(table_idx) => Some(*table_idx), - _ => None, - } - } } impl ExportDesc { @@ -161,10 +123,10 @@ impl ExportDesc { let desc_idx = wasm.read_var_u32()? as usize; let desc = match desc_id { - 0x00 => ExportDesc::FuncIdx(desc_idx), - 0x01 => ExportDesc::TableIdx(desc_idx), - 0x02 => ExportDesc::MemIdx(desc_idx), - 0x03 => ExportDesc::GlobalIdx(desc_idx), + 0x00 => ExportDesc::Func(desc_idx), + 0x01 => ExportDesc::Table(desc_idx), + 0x02 => ExportDesc::Mem(desc_idx), + 0x03 => ExportDesc::Global(desc_idx), other => return Err(ValidationError::MalformedExportDescDiscriminator(other)), }; Ok(desc) diff --git a/src/core/reader/types/import.rs b/src/core/reader/types/import.rs index 494589c7ec..329bfb3ff8 100644 --- a/src/core/reader/types/import.rs +++ b/src/core/reader/types/import.rs @@ -1,6 +1,3 @@ -use alloc::borrow::ToOwned; -use alloc::string::String; - use crate::core::indices::TypeIdx; use crate::core::reader::WasmReader; use crate::{ValidationError, ValidationInfo}; @@ -9,19 +6,16 @@ use super::global::GlobalType; use super::{ExternType, MemType, TableType}; #[derive(Debug, Clone)] -pub struct Import { - #[allow(warnings)] - pub module_name: String, - #[allow(warnings)] - pub name: String, - #[allow(warnings)] +pub struct Import<'wasm> { + pub module_name: &'wasm str, + pub name: &'wasm str, pub desc: ImportDesc, } -impl Import { - pub fn read(wasm: &mut WasmReader) -> Result { - let module_name = wasm.read_name()?.to_owned(); - let name = wasm.read_name()?.to_owned(); +impl<'wasm> Import<'wasm> { + pub fn read(wasm: &mut WasmReader<'wasm>) -> Result { + let module_name = wasm.read_name()?; + let name = wasm.read_name()?; let desc = ImportDesc::read(wasm)?; Ok(Self { diff --git a/src/core/reader/types/mod.rs b/src/core/reader/types/mod.rs index 69f73174c7..73761f3cbe 100644 --- a/src/core/reader/types/mod.rs +++ b/src/core/reader/types/mod.rs @@ -84,7 +84,6 @@ impl RefType { /// /// TODO flatten [NumType] and [RefType] enums, as they are not used individually and `wasmparser` also does it. #[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[allow(clippy::all)] pub enum ValType { NumType(NumType), VecType, diff --git a/src/core/reader/types/values.rs b/src/core/reader/types/values.rs index 12681ec189..85505777d0 100644 --- a/src/core/reader/types/values.rs +++ b/src/core/reader/types/values.rs @@ -19,7 +19,7 @@ const CONTINUATION_BIT: u8 = 0b10000000; const INTEGER_BIT_FLAG: u8 = !CONTINUATION_BIT; -impl WasmReader<'_> { +impl<'wasm> WasmReader<'wasm> { /// Tries to read one byte and fails if the end of file is reached. pub fn read_u8(&mut self) -> Result { let byte = self.peek_u8()?; @@ -351,7 +351,7 @@ impl WasmReader<'_> { } /// Note: If `Err`, the [WasmReader] object is no longer guaranteed to be in a valid state - pub fn read_name(&mut self) -> Result<&str, ValidationError> { + pub fn read_name(&mut self) -> Result<&'wasm str, ValidationError> { let len = self.read_var_u32()? as usize; let utf8_str = &self @@ -369,7 +369,8 @@ impl WasmReader<'_> { mut read_element: F, ) -> Result, ValidationError> where - F: FnMut(&mut WasmReader, usize) -> Result, + T: 'wasm, + F: FnMut(&mut WasmReader<'wasm>, usize) -> Result, { let mut idx = 0; self.read_vec(|wasm| { @@ -382,7 +383,8 @@ impl WasmReader<'_> { /// Note: If `Err`, the [WasmReader] object is no longer guaranteed to be in a valid state pub fn read_vec(&mut self, mut read_element: F) -> Result, ValidationError> where - F: FnMut(&mut WasmReader) -> Result, + T: 'wasm, + F: FnMut(&mut WasmReader<'wasm>) -> Result, { let len = self.read_var_u32()?; core::iter::repeat_with(|| read_element(self)) diff --git a/src/core/slotmap.rs b/src/core/slotmap.rs index a2ae32d376..a5ba0a8b51 100644 --- a/src/core/slotmap.rs +++ b/src/core/slotmap.rs @@ -68,7 +68,7 @@ impl SlotMap { } } - #[allow(unused)] + #[allow(unused)] // reason = "this function might be used in the future" pub fn get(&self, key: &SlotMapKey) -> Option<&T> { let slot = self.slots.get(key.index)?; if slot.generation != key.generation { diff --git a/src/execution/config.rs b/src/execution/config.rs index 660b554da1..1f8fc2c599 100644 --- a/src/execution/config.rs +++ b/src/execution/config.rs @@ -15,9 +15,8 @@ pub trait Config { /// /// This allows the most intricate insight into the interpreters behavior, at the cost of a /// hefty performance penalty - #[allow(unused_variables)] #[inline(always)] - fn instruction_hook(&mut self, bytecode: &[u8], pc: usize) {} + fn instruction_hook(&mut self, _bytecode: &[u8], _pc: usize) {} /// Amount of fuel to be deducted when a single byte `instr` is hit. The cost corresponding to `UNREACHABLE` and /// `END` instructions and other bytes that do not correspond to any Wasm instruction are ignored. diff --git a/src/execution/interpreter_loop.rs b/src/execution/interpreter_loop.rs index 6a3348d409..50ee329853 100644 --- a/src/execution/interpreter_loop.rs +++ b/src/execution/interpreter_loop.rs @@ -225,14 +225,16 @@ pub(super) fn run( .get(current_wasm_func_inst.module_addr) .func_addrs[local_func_idx]; - let func_to_call_ty = store.functions.get(func_to_call_addr).ty(); + let func_to_call_inst = store.functions.get(func_to_call_addr); trace!("Instruction: call [{func_to_call_addr:?}]"); - match store.functions.get(func_to_call_addr) { + match func_to_call_inst { FuncInst::HostFunc(host_func_to_call_inst) => { let params = stack - .pop_tail_iter(func_to_call_ty.params.valtypes.len()) + .pop_tail_iter( + host_func_to_call_inst.function_type.params.valtypes.len(), + ) .collect(); let returns = (host_func_to_call_inst.hostcode)(&mut store.user_data, params); @@ -243,11 +245,16 @@ pub(super) fn run( // Verify that the return parameters match the host function parameters // since we have no validation guarantees for host functions - if returns.len() != func_to_call_ty.returns.valtypes.len() { + if returns.len() + != host_func_to_call_inst.function_type.returns.valtypes.len() + { return Err(RuntimeError::HostFunctionSignatureMismatch); } - for (value, ty) in zip(returns, func_to_call_ty.returns.valtypes) { - if value.to_ty() != ty { + for (value, ty) in zip( + returns, + &host_func_to_call_inst.function_type.returns.valtypes, + ) { + if value.to_ty() != *ty { return Err(RuntimeError::HostFunctionSignatureMismatch); } stack.push_value::(value)?; @@ -258,7 +265,7 @@ pub(super) fn run( stack.push_call_frame::( current_func_addr, - &func_to_call_ty, + &wasm_func_to_call_inst.function_type, remaining_locals, wasm.pc, stp, @@ -320,17 +327,20 @@ pub(super) fn run( Ref::Extern(_) => unreachable_validated!(), }; - let func_to_call_ty = store.functions.get(func_to_call_addr).ty(); - if *func_ty != func_to_call_ty { + let func_to_call_inst = store.functions.get(func_to_call_addr); + + if func_ty != func_to_call_inst.ty() { return Err(TrapError::SignatureMismatch.into()); } trace!("Instruction: call [{func_to_call_addr:?}]"); - match store.functions.get(func_to_call_addr) { + match func_to_call_inst { FuncInst::HostFunc(host_func_to_call_inst) => { let params = stack - .pop_tail_iter(func_to_call_ty.params.valtypes.len()) + .pop_tail_iter( + host_func_to_call_inst.function_type.params.valtypes.len(), + ) .collect(); let returns = (host_func_to_call_inst.hostcode)(&mut store.user_data, params); @@ -341,11 +351,16 @@ pub(super) fn run( // Verify that the return parameters match the host function parameters // since we have no validation guarantees for host functions - if returns.len() != func_to_call_ty.returns.valtypes.len() { + if returns.len() + != host_func_to_call_inst.function_type.returns.valtypes.len() + { return Err(RuntimeError::HostFunctionSignatureMismatch); } - for (value, ty) in zip(returns, func_to_call_ty.returns.valtypes) { - if value.to_ty() != ty { + for (value, ty) in zip( + returns, + &host_func_to_call_inst.function_type.returns.valtypes, + ) { + if value.to_ty() != *ty { return Err(RuntimeError::HostFunctionSignatureMismatch); } stack.push_value::(value)?; @@ -356,7 +371,7 @@ pub(super) fn run( stack.push_call_frame::( current_func_addr, - &func_to_call_ty, + &wasm_func_to_call_inst.function_type, remaining_locals, wasm.pc, stp, @@ -2520,7 +2535,7 @@ pub(super) fn run( DATA_DROP => { decrement_fuel!(T::get_fc_extension_flat_cost(DATA_DROP)); let data_idx = wasm.read_var_u32().unwrap_validated() as DataIdx; - data_drop(&store.modules, &mut store.data, current_module, data_idx)?; + data_drop(&store.modules, &mut store.data, current_module, data_idx); } // See https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-memory-mathsf-memory-copy MEMORY_COPY => { @@ -2663,7 +2678,7 @@ pub(super) fn run( &mut store.elements, current_module, elem_idx, - )?; + ); } // https://webassembly.github.io/spec/core/exec/instructions.html#xref-syntax-instructions-syntax-instr-table-mathsf-table-copy-x-y TABLE_COPY => { @@ -5683,7 +5698,7 @@ pub(super) fn elem_drop( store_elements: &mut AddrVec, current_module: ModuleAddr, elem_idx: usize, -) -> Result<(), RuntimeError> { +) { // WARN: i'm not sure if this is okay or not let elem_addr = *store_modules .get(current_module) @@ -5691,7 +5706,6 @@ pub(super) fn elem_drop( .get(elem_idx) .unwrap_validated(); store_elements.get_mut(elem_addr).references.clear(); - Ok(()) } #[inline(always)] @@ -5737,7 +5751,7 @@ pub(super) fn data_drop( store_data: &mut AddrVec, current_module: ModuleAddr, data_idx: usize, -) -> Result<(), RuntimeError> { +) { // Here is debatable // If we were to be on par with the spec we'd have to use a DataInst struct // But since memory.init is specifically made for Passive data segments @@ -5751,7 +5765,6 @@ pub(super) fn data_drop( .get(data_idx) .unwrap_validated(); store_data.get_mut(data_addr).data.clear(); - Ok(()) } #[inline(always)] diff --git a/src/execution/resumable.rs b/src/execution/resumable.rs index d1f953ce4b..4e1e31917a 100644 --- a/src/execution/resumable.rs +++ b/src/execution/resumable.rs @@ -26,11 +26,6 @@ pub(crate) struct Resumable { pub(crate) struct Dormitory(pub(crate) Arc>>); impl Dormitory { - #[allow(unused)] - pub(crate) fn new() -> Self { - Self::default() - } - pub(crate) fn insert(&self, resumable: Resumable) -> InvokedResumableRef { let key = self.0.write().insert(resumable); @@ -103,7 +98,7 @@ mod test { /// Test that a dormitory can be constructed and that a resumable can be inserted #[test] fn dormitory_constructor() { - let dorm = Dormitory::new(); + let dorm = Dormitory::default(); let empty_stack = Stack::new::<()>( Vec::new(), diff --git a/src/execution/store/instances.rs b/src/execution/store/instances.rs index 9aec96cdc6..71b3c40bb5 100644 --- a/src/execution/store/instances.rs +++ b/src/execution/store/instances.rs @@ -47,10 +47,10 @@ pub struct HostFuncInst { } impl FuncInst { - pub fn ty(&self) -> FuncType { + pub fn ty(&self) -> &FuncType { match self { - FuncInst::WasmFunc(wasm_func_inst) => wasm_func_inst.function_type.clone(), - FuncInst::HostFunc(host_func_inst) => host_func_inst.function_type.clone(), + FuncInst::WasmFunc(wasm_func_inst) => &wasm_func_inst.function_type, + FuncInst::HostFunc(host_func_inst) => &host_func_inst.function_type, } } } diff --git a/src/execution/store/mod.rs b/src/execution/store/mod.rs index ed5f69e57f..511f247bda 100644 --- a/src/execution/store/mod.rs +++ b/src/execution/store/mod.rs @@ -9,7 +9,7 @@ use crate::core::indices::TypeIdx; use crate::core::reader::span::Span; use crate::core::reader::types::data::{DataModeActive, DataSegment}; use crate::core::reader::types::element::{ActiveElem, ElemItems, ElemMode, ElemType}; -use crate::core::reader::types::export::{Export, ExportDesc}; +use crate::core::reader::types::export::ExportDesc; use crate::core::reader::types::global::{Global, GlobalType}; use crate::core::reader::types::{ ExternType, FuncType, ImportSubTypeRelation, MemType, ResultType, TableType, @@ -22,6 +22,7 @@ use crate::resumable::{ Dormitory, FreshResumableRef, InvokedResumableRef, Resumable, ResumableRef, RunState, }; use crate::{RefType, RuntimeError, ValidationInfo}; +use alloc::borrow::ToOwned; use alloc::collections::btree_map::BTreeMap; use alloc::string::String; use alloc::sync::Arc; @@ -287,21 +288,17 @@ impl<'b, T: Config> Store<'b, T> { let export_insts: BTreeMap = module .exports .iter() - .map(|Export { name, desc }| { + .map(|export| { let module_inst = self.modules.get(module_addr); - let value = match desc { - ExportDesc::FuncIdx(func_idx) => { - ExternVal::Func(module_inst.func_addrs[*func_idx]) - } - ExportDesc::TableIdx(table_idx) => { - ExternVal::Table(table_addrs_mod[*table_idx]) - } - ExportDesc::MemIdx(mem_idx) => ExternVal::Mem(mem_addrs_mod[*mem_idx]), - ExportDesc::GlobalIdx(global_idx) => { - ExternVal::Global(module_inst.global_addrs[*global_idx]) + let value = match export.desc { + ExportDesc::Func(func_idx) => ExternVal::Func(module_inst.func_addrs[func_idx]), + ExportDesc::Table(table_idx) => ExternVal::Table(table_addrs_mod[table_idx]), + ExportDesc::Mem(mem_idx) => ExternVal::Mem(mem_addrs_mod[mem_idx]), + ExportDesc::Global(global_idx) => { + ExternVal::Global(module_inst.global_addrs[global_idx]) } }; - (String::from(name), value) + (export.name.to_owned(), value) }) .collect(); @@ -359,14 +356,14 @@ impl<'b, T: Config> Store<'b, T> { s, d, )?; - elem_drop(&self.modules, &mut self.elements, module_addr, i)?; + elem_drop(&self.modules, &mut self.elements, module_addr, i); } ElemMode::Declarative => { // instantiation step 15: // TODO (for now, we are doing hopefully what is equivalent to it) // execute: // elem.drop i - elem_drop(&self.modules, &mut self.elements, module_addr, i)?; + elem_drop(&self.modules, &mut self.elements, module_addr, i); } ElemMode::Passive => (), } @@ -411,7 +408,7 @@ impl<'b, T: Config> Store<'b, T> { s, d, )?; - data_drop(&self.modules, &mut self.data, module_addr, i)?; + data_drop(&self.modules, &mut self.data, module_addr, i); } crate::core::reader::types::data::DataMode::Passive => (), } @@ -515,7 +512,7 @@ impl<'b, T: Config> Store<'b, T> { /// [`Store`] object. pub fn func_type_unchecked(&self, func_addr: FuncAddr) -> FuncType { // 1. Return `S.funcs[a].type`. - self.functions.get(func_addr).ty() + self.functions.get(func_addr).ty().clone() // 2. Post-condition: the returned function type is valid. } @@ -1356,7 +1353,9 @@ impl ExternVal { pub fn extern_type(&self, store: &Store) -> ExternType { match self { // TODO: fix ugly clone in function types - ExternVal::Func(func_addr) => ExternType::Func(store.functions.get(*func_addr).ty()), + ExternVal::Func(func_addr) => { + ExternType::Func(store.functions.get(*func_addr).ty().clone()) + } ExternVal::Table(table_addr) => ExternType::Table(store.tables.get(*table_addr).ty), ExternVal::Mem(mem_addr) => ExternType::Mem(store.memories.get(*mem_addr).ty), ExternVal::Global(global_addr) => { diff --git a/src/lib.rs b/src/lib.rs index 0422eda341..e26ca4c704 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,8 +11,8 @@ extern crate log_wrapper; pub use core::error::ValidationError; pub use core::reader::types::{ - export::ExportDesc, global::GlobalType, ExternType, FuncType, Limits, MemType, NumType, - RefType, ResultType, TableType, ValType, + global::GlobalType, ExternType, FuncType, Limits, MemType, NumType, RefType, ResultType, + TableType, ValType, }; pub use core::rw_spinlock; pub use execution::error::{RuntimeError, TrapError}; diff --git a/src/validation/mod.rs b/src/validation/mod.rs index 7b5c5c01ad..12025b373c 100644 --- a/src/validation/mod.rs +++ b/src/validation/mod.rs @@ -8,13 +8,13 @@ use crate::core::reader::section_header::{SectionHeader, SectionTy}; use crate::core::reader::span::Span; use crate::core::reader::types::data::DataSegment; use crate::core::reader::types::element::ElemType; -use crate::core::reader::types::export::Export; +use crate::core::reader::types::export::{Export, ExportDesc}; use crate::core::reader::types::global::{Global, GlobalType}; use crate::core::reader::types::import::{Import, ImportDesc}; use crate::core::reader::types::{ExternType, FuncType, MemType, ResultType, TableType}; use crate::core::reader::WasmReader; use crate::core::sidetable::Sidetable; -use crate::{ExportDesc, ValidationError}; +use crate::ValidationError; pub(crate) mod code; pub(crate) mod data; @@ -41,13 +41,12 @@ pub(crate) struct ImportsLength { pub struct ValidationInfo<'bytecode> { pub(crate) wasm: &'bytecode [u8], pub(crate) types: Vec, - pub(crate) imports: Vec, + pub(crate) imports: Vec>, pub(crate) functions: Vec, pub(crate) tables: Vec, pub(crate) memories: Vec, pub(crate) globals: Vec, - #[allow(dead_code)] - pub(crate) exports: Vec, + pub(crate) exports: Vec>, /// Each block contains the validated code section and the stp corresponding to /// the beginning of that code section pub(crate) func_blocks_stps: Vec<(Span, usize)>, @@ -64,12 +63,12 @@ fn validate_exports(validation_info: &ValidationInfo) -> Result<(), ValidationEr let mut found_export_names: btree_set::BTreeSet<&str> = btree_set::BTreeSet::new(); use crate::core::reader::types::export::ExportDesc::*; for export in &validation_info.exports { - if found_export_names.contains(export.name.as_str()) { + if found_export_names.contains(export.name) { return Err(ValidationError::DuplicateExportName); } - found_export_names.insert(export.name.as_str()); + found_export_names.insert(export.name); match export.desc { - FuncIdx(func_idx) => { + Func(func_idx) => { if validation_info.functions.len() + validation_info.imports_length.imported_functions <= func_idx @@ -77,21 +76,21 @@ fn validate_exports(validation_info: &ValidationInfo) -> Result<(), ValidationEr return Err(ValidationError::InvalidFuncIdx(func_idx)); } } - TableIdx(table_idx) => { + Table(table_idx) => { if validation_info.tables.len() + validation_info.imports_length.imported_tables <= table_idx { return Err(ValidationError::InvalidTableIdx(table_idx)); } } - MemIdx(mem_idx) => { + Mem(mem_idx) => { if validation_info.memories.len() + validation_info.imports_length.imported_memories <= mem_idx { return Err(ValidationError::InvalidMemIdx(mem_idx)); } } - GlobalIdx(global_idx) => { + Global(global_idx) => { if validation_info.globals.len() + validation_info.imports_length.imported_globals <= global_idx { @@ -237,6 +236,13 @@ pub fn validate(wasm: &[u8]) -> Result, ValidationError> { .chain(local_functions.iter().cloned()) .collect::>(); + // All functions need to be addressable by a u32 + if all_functions.len() + > usize::try_from(u32::MAX).expect("pointer width to be at least 32 bits") + { + return Err(ValidationError::TooManyFunctions); + } + while (skip_section(&mut wasm, &mut header)?).is_some() {} let imported_tables = imports @@ -257,6 +263,11 @@ pub fn validate(wasm: &[u8]) -> Result, ValidationError> { temp }; + // All tables need to be addressable by a u32 + if all_tables.len() > usize::try_from(u32::MAX).expect("pointer width to be at least 32 bits") { + return Err(ValidationError::TooManyTables); + } + while (skip_section(&mut wasm, &mut header)?).is_some() {} let imported_memories = imports @@ -277,10 +288,18 @@ pub fn validate(wasm: &[u8]) -> Result, ValidationError> { temp.extend(memories.clone()); temp }; + if all_memories.len() > 1 { return Err(ValidationError::UnsupportedMultipleMemoriesProposal); } + // Note: Without the multiple memory proposal, this is dead code due to the previous check. + // All memories need to be addressable by a u32 + if all_memories.len() > usize::try_from(u32::MAX).expect("pointer width to be at least 32 bits") + { + return Err(ValidationError::TooManyMemories); + } + while (skip_section(&mut wasm, &mut header)?).is_some() {} // we start off with the imported globals @@ -313,6 +332,12 @@ pub fn validate(wasm: &[u8]) -> Result, ValidationError> { all_globals.push(*item) } + // All globals need to be addressable by a u32 + if all_globals.len() > usize::try_from(u32::MAX).expect("pointer width to be at least 32 bits") + { + return Err(ValidationError::TooManyGlobals); + } + while (skip_section(&mut wasm, &mut header)?).is_some() {} let exports = handle_section(&mut wasm, &mut header, SectionTy::Export, |wasm, _| { @@ -321,7 +346,7 @@ pub fn validate(wasm: &[u8]) -> Result, ValidationError> { .unwrap_or_default(); validation_context_refs.extend(exports.iter().filter_map( |Export { name: _, desc }| match *desc { - ExportDesc::FuncIdx(func_idx) => Some(func_idx), + ExportDesc::Func(func_idx) => Some(func_idx), _ => None, }, )); @@ -465,12 +490,16 @@ fn read_next_header( } #[inline(always)] -fn handle_section Result>( - wasm: &mut WasmReader, +fn handle_section<'wasm, T, F>( + wasm: &mut WasmReader<'wasm>, header: &mut Option, section_ty: SectionTy, handler: F, -) -> Result, ValidationError> { +) -> Result, ValidationError> +where + T: 'wasm, + F: FnOnce(&mut WasmReader<'wasm>, SectionHeader) -> Result, +{ match &header { Some(SectionHeader { ty, .. }) if *ty == section_ty => { let h = header.take().unwrap(); @@ -483,19 +512,21 @@ fn handle_section Result { +impl<'wasm> ValidationInfo<'wasm> { /// Returns the imports of this module as an iterator. Each import consist /// of a module name, a name and an extern type. /// /// See: WebAssembly Specification 2.0 - 7.1.5 - module_imports pub fn imports<'a>( &'a self, - ) -> Map, impl FnMut(&'a Import) -> (&'a str, &'a str, ExternType)> - { + ) -> Map< + core::slice::Iter<'a, Import<'wasm>>, + impl FnMut(&'a Import<'wasm>) -> (&'a str, &'a str, ExternType), + > { self.imports.iter().map(|import| { ( - &*import.module_name, - &*import.name, + import.module_name, + import.name, import.desc.extern_type(self), ) }) @@ -507,9 +538,12 @@ impl ValidationInfo<'_> { /// See: WebAssembly Specification 2.0 - 7.1.5 - module_exports pub fn exports<'a>( &'a self, - ) -> Map, impl FnMut(&'a Export) -> (&'a str, ExternType)> { + ) -> Map< + core::slice::Iter<'a, Export<'wasm>>, + impl FnMut(&'a Export<'wasm>) -> (&'a str, ExternType), + > { self.exports .iter() - .map(|export| (&*export.name, export.desc.extern_type(self))) + .map(|export| (export.name, export.desc.extern_type(self))) } } diff --git a/tests/memory_as_slice.rs b/tests/memory_as_slice.rs index 30c90eba36..8585fd5145 100644 --- a/tests/memory_as_slice.rs +++ b/tests/memory_as_slice.rs @@ -11,7 +11,7 @@ fn simple_byte_writes() { store .mem_access_mut_slice(mem, |mem_as_slice| { - for (n, x) in mem_as_slice.into_iter().enumerate() { + for (n, x) in mem_as_slice.iter_mut().enumerate() { *x = u8::try_from(n % 256).expect("this to never be larger than 255"); } }) @@ -38,7 +38,7 @@ fn interpret_as_str() { // Read the string again and check if it is equal to the original one store .mem_access_mut_slice(mem, |mem_as_slice| { - let bytes = &mem_as_slice[0..STR_TO_WRITE.as_bytes().len()]; + let bytes = &mem_as_slice[0..STR_TO_WRITE.len()]; let as_str = std::str::from_utf8(bytes).unwrap(); assert_eq!(as_str, STR_TO_WRITE); }) diff --git a/tests/specification/reports.rs b/tests/specification/reports.rs index 264358b219..4af8a42096 100644 --- a/tests/specification/reports.rs +++ b/tests/specification/reports.rs @@ -148,9 +148,7 @@ pub struct ScriptError { /// Boxed because of struct size pub error: Box, pub context: String, - #[allow(unused)] pub line_number: Option, - #[allow(unused)] pub command: Option, }