From 7f1bb6436f04f18b580e41ece5e8987702b56bfb Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Wed, 31 Jan 2024 20:45:00 +0100 Subject: [PATCH] deps: Unify wasmparser usage to a single version Upgrade the wasmparser version used by all crates to 0.121.0, and switch to a shared workspace dependency. --- Cargo.lock | 19 +-- lib/api/Cargo.toml | 4 +- lib/api/src/js/module_info_polyfill.rs | 100 +++++++----- lib/registry/Cargo.toml | 2 +- lib/registry/src/package/builder.rs | 42 ++--- lib/wasm-interface/Cargo.toml | 2 +- lib/wasm-interface/src/validate.rs | 208 ++++++++++++++----------- 7 files changed, 199 insertions(+), 178 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3abbf40c748..3c4895caf54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6299,8 +6299,7 @@ dependencies = [ "wasmer-derive", "wasmer-types", "wasmer-vm", - "wasmparser 0.83.0", - "wasmparser 0.95.0", + "wasmparser 0.121.0", "wat", "winapi 0.3.9", ] @@ -6776,7 +6775,7 @@ dependencies = [ "url", "wasmer-toml", "wasmer-wasm-interface", - "wasmparser 0.51.4", + "wasmparser 0.121.0", "whoami", ] @@ -6983,7 +6982,7 @@ dependencies = [ "either", "nom", "serde", - "wasmparser 0.51.4", + "wasmparser 0.121.0", "wat", ] @@ -7038,18 +7037,6 @@ dependencies = [ "wasmer-wast", ] -[[package]] -name = "wasmparser" -version = "0.51.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a" - -[[package]] -name = "wasmparser" -version = "0.83.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" - [[package]] name = "wasmparser" version = "0.95.0" diff --git a/lib/api/Cargo.toml b/lib/api/Cargo.toml index 2c9268b6dc4..8efc21d3f04 100644 --- a/lib/api/Cargo.toml +++ b/lib/api/Cargo.toml @@ -52,7 +52,7 @@ wasmer-compiler-llvm = { path = "../compiler-llvm", version = "=4.2.5", optional wasm-bindgen = { version = "0.2.74", optional = true } js-sys = { version = "0.3.51", optional = true } rusty_jsc = { version = "0.1.0", optional = true } -wasmparser = { version = "0.83", default-features = false, optional = true } +wasmparser = { workspace = true, default-features = false, optional = true } # - Mandatory dependencies for `sys` on Windows. [target.'cfg(all(not(target_arch = "wasm32"), target_os = "windows"))'.dependencies] @@ -72,7 +72,7 @@ wasm-bindgen = "0.2.74" js-sys = "0.3.51" wasmer-derive = { path = "../derive", version = "=4.2.5" } # - Optional dependencies for `js`. -wasmparser = { version = "0.95", default-features = false, optional = true } +wasmparser = { workspace = true, default-features = false, optional = true } hashbrown = { version = "0.11", optional = true } serde-wasm-bindgen = { version = "0.4.5" } serde = { version = "1.0", features = ["derive"] } diff --git a/lib/api/src/js/module_info_polyfill.rs b/lib/api/src/js/module_info_polyfill.rs index 5ddf5746be3..43c3aa3181f 100644 --- a/lib/api/src/js/module_info_polyfill.rs +++ b/lib/api/src/js/module_info_polyfill.rs @@ -288,8 +288,7 @@ pub fn translate_module<'data>(data: &'data [u8]) -> WasmResult WasmResult { wasmparser::ValType::F32 => Ok(Type::F32), wasmparser::ValType::F64 => Ok(Type::F64), wasmparser::ValType::V128 => Ok(Type::V128), - wasmparser::ValType::ExternRef => Ok(Type::ExternRef), - wasmparser::ValType::FuncRef => Ok(Type::FuncRef), + wasmparser::ValType::Ref(ty) => wpreftype_to_type(ty), + } +} + +/// Converts a wasmparser ref type to a [`Type`]. +pub fn wpreftype_to_type(ty: wasmparser::RefType) -> WasmResult { + if ty.is_extern_ref() { + Ok(Type::ExternRef) + } else if ty.is_func_ref() { + Ok(Type::FuncRef) + } else { + Err(format!("Unsupported ref type: {:?}", ty)) } } /// Parses the Type section of the wasm module. pub fn parse_type_section( - types: TypeSectionReader, + reader: TypeSectionReader, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - let count = types.get_count(); - module_info.reserve_signatures(count)?; - - for entry in types { - if let Ok(wasmparser::Type::Func(functype)) = entry { - let params = functype.params(); - let returns = functype.results(); - let sig_params: Vec = params - .iter() - .map(|ty| { - wptype_to_type(*ty) - .expect("only numeric types are supported in function signatures") - }) - .collect(); - let sig_returns: Vec = returns - .iter() - .map(|ty| { - wptype_to_type(*ty) - .expect("only numeric types are supported in function signatures") - }) - .collect(); - let sig = FunctionType::new(sig_params, sig_returns); - module_info.declare_signature(sig)?; - } else { - unimplemented!("module linking not implemented yet") + module_info.reserve_signatures(reader.count())?; + + for res in reader { + let group = res.map_err(transform_err)?; + + for ty in group.into_types() { + match ty.composite_type { + wasmparser::CompositeType::Func(functype) => { + let params = functype.params(); + let returns = functype.results(); + let sig_params: Vec = params + .iter() + .map(|ty| { + wptype_to_type(*ty) + .expect("only numeric types are supported in function signatures") + }) + .collect(); + let sig_returns: Vec = returns + .iter() + .map(|ty| { + wptype_to_type(*ty) + .expect("only numeric types are supported in function signatures") + }) + .collect(); + let sig = FunctionType::new(sig_params, sig_returns); + module_info.declare_signature(sig)?; + } + _ => { + unimplemented!("GC is not implemented yet") + } + } } } @@ -356,7 +371,7 @@ pub fn parse_import_section<'data>( imports: ImportSectionReader<'data>, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - module_info.reserve_imports(imports.get_count())?; + module_info.reserve_imports(imports.count())?; for entry in imports { let import = entry.map_err(transform_err)?; @@ -406,7 +421,7 @@ pub fn parse_import_section<'data>( TypeRef::Table(ref tab) => { module_info.declare_table_import( TableType { - ty: wptype_to_type(tab.element_type).unwrap(), + ty: wpreftype_to_type(tab.element_type).unwrap(), minimum: tab.initial, maximum: tab.maximum, }, @@ -424,7 +439,7 @@ pub fn parse_function_section( functions: FunctionSectionReader, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - let num_functions = functions.get_count(); + let num_functions = functions.count(); module_info.reserve_func_types(num_functions)?; for entry in functions { @@ -440,14 +455,14 @@ pub fn parse_table_section( tables: TableSectionReader, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - module_info.reserve_tables(tables.get_count())?; + module_info.reserve_tables(tables.count())?; for entry in tables { let table = entry.map_err(transform_err)?; module_info.declare_table(TableType { - ty: wptype_to_type(table.element_type).unwrap(), - minimum: table.initial, - maximum: table.maximum, + ty: wpreftype_to_type(table.ty.element_type).unwrap(), + minimum: table.ty.initial, + maximum: table.ty.maximum, })?; } @@ -459,7 +474,7 @@ pub fn parse_memory_section( memories: MemorySectionReader, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - module_info.reserve_memories(memories.get_count())?; + module_info.reserve_memories(memories.count())?; for entry in memories { let WPMemoryType { @@ -486,7 +501,7 @@ pub fn parse_global_section( globals: GlobalSectionReader, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - module_info.reserve_globals(globals.get_count())?; + module_info.reserve_globals(globals.count())?; for entry in globals { let WPGlobalType { @@ -508,7 +523,7 @@ pub fn parse_export_section<'data>( exports: ExportSectionReader<'data>, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - module_info.reserve_exports(exports.get_count())?; + module_info.reserve_exports(exports.count())?; for entry in exports { let Export { @@ -553,7 +568,7 @@ pub fn parse_name_section<'data>( mut names: NameSectionReader<'data>, module_info: &mut ModuleInfoPolyfill, ) -> WasmResult<()> { - while let Ok(subsection) = names.read() { + while let Some(Ok(subsection)) = names.next() { match subsection { wasmparser::Name::Function(_function_subsection) => { //for naming in function_subsection.into_iter().flatten() { @@ -579,6 +594,7 @@ pub fn parse_name_section<'data>( | wasmparser::Name::Global(_) | wasmparser::Name::Element(_) | wasmparser::Name::Data(_) + | wasmparser::Name::Tag(_) | wasmparser::Name::Unknown { .. } => {} }; } @@ -589,7 +605,7 @@ pub fn parse_name_section<'data>( // mut naming_reader: NamingReader<'_>, // ) -> Option> { // let mut function_names = HashMap::new(); -// for _ in 0..naming_reader.get_count() { +// for _ in 0..naming_reader.count() { // let Naming { index, name } = naming_reader.read().ok()?; // if index == std::u32::MAX { // // We reserve `u32::MAX` for our own use. diff --git a/lib/registry/Cargo.toml b/lib/registry/Cargo.toml index 0c71ad4fa00..ba8693f14ab 100644 --- a/lib/registry/Cargo.toml +++ b/lib/registry/Cargo.toml @@ -50,7 +50,7 @@ tracing = "0.1.40" url = "2.3.1" wasmer-toml = { workspace = true } wasmer-wasm-interface = { version = "4.2.5", path = "../wasm-interface", optional = true } -wasmparser = { version = "0.51.4", optional = true } +wasmparser = { workspace = true, optional = true } whoami = "1.2.3" [dev-dependencies] diff --git a/lib/registry/src/package/builder.rs b/lib/registry/src/package/builder.rs index 326db4d28ae..ab804f0f34d 100644 --- a/lib/registry/src/package/builder.rs +++ b/lib/registry/src/package/builder.rs @@ -786,32 +786,22 @@ mod validate { wasm: &[u8], file_name: String, ) -> Result<(), ValidationError> { - use wasmparser::WasmDecoder; - let mut parser = wasmparser::ValidatingParser::new( - wasm, - Some(wasmparser::ValidatingParserConfig { - operator_config: wasmparser::OperatorValidatorConfig { - enable_threads: true, - enable_reference_types: true, - enable_simd: true, - enable_bulk_memory: true, - enable_multi_value: true, - }, - }), - ); - loop { - let state = parser.read(); - match state { - wasmparser::ParserState::EndWasm => return Ok(()), - wasmparser::ParserState::Error(e) => { - return Err(ValidationError::InvalidWasm { - file: file_name, - error: format!("{}", e), - }); - } - _ => {} - } - } + let mut val = wasmparser::Validator::new_with_features(wasmparser::WasmFeatures { + threads: true, + reference_types: true, + simd: true, + bulk_memory: true, + multi_value: true, + ..Default::default() + }); + + val.validate_all(wasm) + .map_err(|e| ValidationError::InvalidWasm { + file: file_name.clone(), + error: format!("{}", e), + })?; + + Ok(()) } /// How should validation be treated by the publishing process? diff --git a/lib/wasm-interface/Cargo.toml b/lib/wasm-interface/Cargo.toml index 479fd1bd68c..c071307414d 100644 --- a/lib/wasm-interface/Cargo.toml +++ b/lib/wasm-interface/Cargo.toml @@ -15,7 +15,7 @@ bincode = { version = "1", optional = true } either = "1.5" nom = "5" serde = { version = "1", features = ["derive"] } -wasmparser = { version = "0.51.4", optional = true } +wasmparser = { workspace = true, optional = true } [dev-dependencies] wat = "1.0" diff --git a/lib/wasm-interface/src/validate.rs b/lib/wasm-interface/src/validate.rs index c25bb70223a..7f371057202 100644 --- a/lib/wasm-interface/src/validate.rs +++ b/lib/wasm-interface/src/validate.rs @@ -6,14 +6,12 @@ use crate::{Export, Import, Interface, WasmType}; use std::collections::HashMap; -use wasmparser::{ExternalKind, FuncType, GlobalType, ImportSectionEntryType}; +use wasmparser::{CompositeType, ExternalKind, FuncType, GlobalType, Payload, TypeRef}; pub fn validate_wasm_and_report_errors( wasm: &[u8], interface: &Interface, ) -> Result<(), WasmValidationError> { - use wasmparser::WasmDecoder; - let mut errors: Vec = vec![]; let mut import_fns: HashMap<(String, String), u32> = HashMap::new(); let mut export_fns: HashMap = HashMap::new(); @@ -22,90 +20,122 @@ pub fn validate_wasm_and_report_errors( let mut global_types: Vec = vec![]; let mut fn_sigs: Vec = vec![]; - let mut parser = wasmparser::ValidatingParser::new( - wasm, - Some(wasmparser::ValidatingParserConfig { - operator_config: wasmparser::OperatorValidatorConfig { - enable_threads: true, - enable_reference_types: true, - enable_simd: true, - enable_bulk_memory: true, - enable_multi_value: true, - }, - }), - ); - loop { - let state = parser.read(); - match state { - wasmparser::ParserState::EndWasm => break, - wasmparser::ParserState::Error(e) => { - return Err(WasmValidationError::InvalidWasm { - error: format!("{}", e), - }); - } - wasmparser::ParserState::ImportSectionEntry { - module, - field, - ref ty, - } => match ty { - ImportSectionEntryType::Function(idx) => { - import_fns.insert(Import::format_key(module, field), *idx); - fn_sigs.push(*idx); - } - ImportSectionEntryType::Global(GlobalType { content_type, .. }) => { - let global_type = - wasmparser_type_into_wasm_type(*content_type).map_err(|err| { - WasmValidationError::UnsupportedType { - error: format!( - "Invalid type found in import \"{}\" \"{}\": {}", - module, field, err - ), - } - })?; - if let Some(val) = interface.imports.get(&Import::format_key(module, field)) { - if let Import::Global { var_type, .. } = val { - if *var_type != global_type { + let mut val = wasmparser::Validator::new_with_features(wasmparser::WasmFeatures { + threads: true, + reference_types: true, + simd: true, + bulk_memory: true, + multi_value: true, + ..Default::default() + }); + + val.validate_all(wasm) + .map_err(|e| WasmValidationError::InvalidWasm { + error: format!("{}", e), + })?; + + let parser = wasmparser::Parser::new(0).parse_all(wasm); + + for res in parser { + let payload = res.map_err(|e| WasmValidationError::InvalidWasm { + error: format!("{}", e), + })?; + match payload { + Payload::End(_) => break, + Payload::ImportSection(reader) => { + for item in reader.into_iter_with_offsets() { + let (_offset, import) = item.map_err(|e| WasmValidationError::InvalidWasm { + error: format!("{}", e), + })?; + + match import.ty { + TypeRef::Func(idx) => { + import_fns.insert(Import::format_key(&import.module, import.name), idx); + fn_sigs.push(idx); + } + TypeRef::Global(GlobalType { content_type, .. }) => { + let global_type = wasmparser_type_into_wasm_type(content_type) + .map_err(|err| WasmValidationError::UnsupportedType { + error: format!( + "Invalid type found in import \"{}\" \"{}\": {}", + import.module, import.name, err + ), + })?; + if let Some(val) = interface + .imports + .get(&Import::format_key(import.module, import.name)) + { + if let Import::Global { var_type, .. } = val { + if *var_type != global_type { + errors.push(format!( + "Invalid type on Global \"{}\". Expected {} found {}", + import.name, var_type, global_type + )); + } + } else { + errors.push(format!( + "Invalid import type. Expected Global, found {:?}", + val + )); + } + } else { errors.push(format!( - "Invalid type on Global \"{}\". Expected {} found {}", - field, var_type, global_type + "Global import \"{}\" not found in the specified interface", + import.name )); } - } else { - errors.push(format!( - "Invalid import type. Expected Global, found {:?}", - val - )); } - } else { - errors.push(format!( - "Global import \"{}\" not found in the specified interface", - field - )); + _ => (), } } - _ => (), - }, - wasmparser::ParserState::ExportSectionEntry { - field, - index, - ref kind, - } => match kind { - ExternalKind::Function => { - export_fns.insert(Export::format_key(field), *index); + } + Payload::ExportSection(reader) => { + for res in reader.into_iter() { + let export = res.map_err(|e| WasmValidationError::InvalidWasm { + error: format!("{}", e), + })?; + + match export.kind { + ExternalKind::Func => { + export_fns.insert(Export::format_key(export.name), export.index); + } + ExternalKind::Global => { + export_globals.insert(Export::format_key(export.name), export.index); + } + _ => (), + } } - ExternalKind::Global => { - export_globals.insert(Export::format_key(field), *index); + } + Payload::GlobalSection(reader) => { + for res in reader.into_iter() { + let global = res.map_err(|e| WasmValidationError::InvalidWasm { + error: format!("{}", e), + })?; + + global_types.push(global.ty); } - _ => (), - }, - wasmparser::ParserState::BeginGlobalSectionEntry(gt) => { - global_types.push(*gt); } - wasmparser::ParserState::TypeSectionEntry(ft) => { - type_defs.push(ft.clone()); + Payload::TypeSection(reader) => { + for res in reader.into_iter() { + let group = res.map_err(|e| WasmValidationError::InvalidWasm { + error: format!("{}", e), + })?; + + for ty in group.into_types() { + if let CompositeType::Func(ft) = ty.composite_type { + type_defs.push(ft); + } + } + } } - wasmparser::ParserState::FunctionSectionEntry(n) => { - fn_sigs.push(*n); + Payload::FunctionSection(reader) => { + for res in reader.into_iter() { + let func = res.map_err(|e| WasmValidationError::InvalidWasm { + error: format!("{}", e), + })?; + + fn_sigs.push(func); + } } _ => {} } @@ -142,9 +172,8 @@ fn validate_imports( continue; }; if let Import::Func { params, result, .. } = interface_def { - debug_assert!(type_sig.form == wasmparser::Type::Func); for (i, param) in type_sig - .params + .params() .iter() .cloned() .map(wasmparser_type_into_wasm_type) @@ -170,7 +199,7 @@ fn validate_imports( } } for (i, ret) in type_sig - .returns + .results() .iter() .cloned() .map(wasmparser_type_into_wasm_type) @@ -236,9 +265,8 @@ fn validate_export_fns( continue; }; if let Export::Func { params, result, .. } = interface_def { - debug_assert!(type_sig.form == wasmparser::Type::Func); for (i, param) in type_sig - .params + .params() .iter() .cloned() .map(wasmparser_type_into_wasm_type) @@ -247,7 +275,7 @@ fn validate_export_fns( match param { Ok(t) => { if params.get(i).is_none() { - errors.push(format!("Found {} args but the interface only expects {} for exported function \"{}\"", type_sig.params.len(), params.len(), &key)); + errors.push(format!("Found {} args but the interface only expects {} for exported function \"{}\"", type_sig.params().len(), params.len(), &key)); continue 'export_loop; } if t != params[i] { @@ -262,7 +290,7 @@ fn validate_export_fns( } } for (i, ret) in type_sig - .returns + .results() .iter() .cloned() .map(wasmparser_type_into_wasm_type) @@ -328,13 +356,13 @@ fn validate_export_globals( /// /// Additionally wasmerparser containers more advanced types like references that /// wasm-interface does not yet support -fn wasmparser_type_into_wasm_type(ty: wasmparser::Type) -> Result { - use wasmparser::Type; +fn wasmparser_type_into_wasm_type(ty: wasmparser::ValType) -> Result { + use wasmparser::ValType; Ok(match ty { - Type::I32 => WasmType::I32, - Type::I64 => WasmType::I64, - Type::F32 => WasmType::F32, - Type::F64 => WasmType::F64, + ValType::I32 => WasmType::I32, + ValType::I64 => WasmType::I64, + ValType::F32 => WasmType::F32, + ValType::F64 => WasmType::F64, e => { return Err(format!("Invalid type found: {:?}", e)); }