diff --git a/packages/vm/src/compatibility.rs b/packages/vm/src/compatibility.rs index 9cfdc4dfe1..e11300d188 100644 --- a/packages/vm/src/compatibility.rs +++ b/packages/vm/src/compatibility.rs @@ -5,15 +5,14 @@ use wasmer::wasmparser::Export; use wasmer::wasmparser::Import; use wasmer::wasmparser::ImportSectionReader; use wasmer::wasmparser::MemorySectionReader; -use wasmer::wasmparser::Parser; use wasmer::wasmparser::Payload; use wasmer::wasmparser::TableSectionReader; use wasmer::wasmparser::TypeRef; -use wasmer::wasmparser::Validator; use crate::capabilities::required_capabilities_from_module; use crate::errors::{VmError, VmResult}; use crate::limited::LimitedDisplay; +use crate::static_analysis::validate_wasm; use crate::static_analysis::ExportInfo; /// Lists all imports we provide upon instantiating the instance in Instance::from_module() @@ -81,15 +80,8 @@ const MAX_IMPORTS: u32 = 100; /// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports) pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet) -> VmResult<()> { - let parsed = Parser::new(0).parse_all(wasm_code); - let mut validator = Validator::new(); - // TODO: some of the validator checks are duplicated in our checks below - let mut memory_section: Option> = None; - for payload in parsed { - let payload = payload?; - validator.payload(&payload)?; - + validate_wasm(wasm_code, |payload| { match payload { Payload::TableSection(t) => check_wasm_tables(t)?, Payload::MemorySection(m) => memory_section = Some(m), @@ -102,7 +94,9 @@ pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet) -> Payload::ImportSection(i) => check_wasm_imports(i, SUPPORTED_IMPORTS)?, _ => {} } - } + Ok(()) + })?; + // we want to fail if there is no memory section, so this check is delayed until the end check_wasm_memories(memory_section)?; Ok(()) diff --git a/packages/vm/src/static_analysis.rs b/packages/vm/src/static_analysis.rs index ddf55195e2..74a2dfe578 100644 --- a/packages/vm/src/static_analysis.rs +++ b/packages/vm/src/static_analysis.rs @@ -1,6 +1,9 @@ use std::collections::HashSet; -use wasmer::wasmparser::{Export, ExportSectionReader, ExternalKind}; +use wasmer::wasmparser::{ + Export, ExportSectionReader, ExternalKind, Parser, Payload, ValidPayload, Validator, + WasmFeatures, +}; use crate::errors::VmResult; @@ -13,29 +16,56 @@ pub const REQUIRED_IBC_EXPORTS: &[&str] = &[ "ibc_packet_timeout", ]; +/// Validates the given wasm code and calls the callback for each payload. +/// "Validates" in this case refers to general WebAssembly validation, not specific to CosmWasm. +pub fn validate_wasm<'a>( + wasm_code: &'a [u8], + mut handle_payload: impl FnMut(Payload<'a>) -> VmResult<()>, +) -> VmResult<()> { + let mut validator = Validator::new_with_features(WasmFeatures { + mutable_global: false, + saturating_float_to_int: false, + sign_extension: true, + reference_types: true, + multi_value: false, + bulk_memory: false, + component_model: false, + simd: false, + relaxed_simd: false, + threads: false, + tail_call: false, + deterministic_only: true, + multi_memory: false, + exceptions: false, + memory64: false, + extended_const: false, + }); + + for p in Parser::new(0).parse_all(wasm_code) { + let p = p?; + // validate the payload + if let ValidPayload::Func(fv, body) = validator.payload(&p)? { + // also validate function bodies + fv.into_validator(Default::default()).validate(&body)?; + } + // tell caller about the payload + handle_payload(p)?; + } + + Ok(()) +} + /// A small helper macro to validate the wasm module and extract a reader for a specific section. macro_rules! extract_reader { ($wasm_code: expr, $payload: ident, $t: ty) => {{ - fn extract(wasm_code: &[u8]) -> crate::VmResult> { - use wasmer::wasmparser::{Parser, Payload, ValidPayload, Validator}; - - let mut validator = Validator::new(); - let parser = Parser::new(0); - + fn extract(wasm_code: &[u8]) -> $crate::VmResult> { let mut value = None; - for p in parser.parse_all(wasm_code) { - let p = p?; - // validate the payload - if let ValidPayload::Func(mut fv, body) = validator.payload(&p)? { - // also validate function bodies - fv.validate(&body)?; - } - if let Payload::$payload(e) = p { - // do not return immediately, as we want to validate the entire module - value = Some(e); + $crate::static_analysis::validate_wasm(wasm_code, |p| { + if let Payload::$payload(p) = p { + value = Some(p); } - } - + Ok(()) + })?; Ok(value) }