diff --git a/Cargo.lock b/Cargo.lock index cf6c7949af..a0a1b39ce4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,9 +366,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -736,9 +736,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "foldhash", ] @@ -791,7 +791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "serde", ] @@ -860,9 +860,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -890,9 +890,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.164" +version = "0.2.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" +checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" [[package]] name = "libfuzzer-sys" @@ -984,7 +984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "crc32fast", - "hashbrown 0.15.1", + "hashbrown 0.15.2", "indexmap", "memchr", ] @@ -1063,9 +1063,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -1190,9 +1190,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags", "errno", @@ -1321,7 +1321,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a3275464d7a9f2d4cac57c89c2ef96a8524dba2864c8d6f82e3980baf136f9b" dependencies = [ - "hashbrown 0.15.1", + "hashbrown 0.15.2", "serde", ] @@ -1333,9 +1333,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -1411,9 +1411,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1422,9 +1422,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -1433,9 +1433,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] @@ -1448,9 +1448,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-width" @@ -1599,6 +1599,15 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de35b6c3ef1f53ac7a31b5e69bc00f1542ea337e7e7162dc34c68b537ff82690" +dependencies = [ + "leb128", +] + [[package]] name = "wasm-smith" version = "0.220.0" @@ -1641,7 +1650,7 @@ dependencies = [ "wasmi_core 0.39.1", "wasmi_ir", "wasmi_wast", - "wasmparser-nostd", + "wasmparser 0.221.0", "wat", ] @@ -1690,7 +1699,7 @@ dependencies = [ name = "wasmi_collections" version = "0.39.1" dependencies = [ - "hashbrown 0.15.1", + "hashbrown 0.15.2", "string-interner", ] @@ -1779,6 +1788,17 @@ dependencies = [ "indexmap", ] +[[package]] +name = "wasmparser" +version = "0.221.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8659e755615170cfe20da468865c989da78c5da16d8652e69a75acda02406a92" +dependencies = [ + "bitflags", + "hashbrown 0.15.2", + "indexmap", +] + [[package]] name = "wasmparser-nostd" version = "0.100.2" @@ -1992,13 +2012,26 @@ dependencies = [ "wasm-encoder 0.220.0", ] +[[package]] +name = "wast" +version = "221.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8eb1933d493dd07484a255c3f52236123333f5befaa3be36182a50d393ec54" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.221.0", +] + [[package]] name = "wat" -version = "1.220.0" +version = "1.221.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de4f1d7d59614ba690541360102b995c4eb1b9ed373701d5102cc1a968b1c5a3" +checksum = "c813fd4e5b2b97242830b56e7b7dc5479bc17aaa8730109be35e61909af83993" dependencies = [ - "wast 220.0.0", + "wast 221.0.0", ] [[package]] diff --git a/crates/wasmi/Cargo.toml b/crates/wasmi/Cargo.toml index 8770543b39..af1ff3d676 100644 --- a/crates/wasmi/Cargo.toml +++ b/crates/wasmi/Cargo.toml @@ -19,7 +19,7 @@ exclude = [ ] [dependencies] -wasmparser = { version = "0.100.2", package = "wasmparser-nostd", default-features = false } +wasmparser = { version = "0.221.0", default-features = false, features = ["validate", "features"] } wasmi_core = { workspace = true } wasmi_collections = { workspace = true } wasmi_ir = { workspace = true } @@ -48,8 +48,14 @@ std = [ "spin/std", "arrayvec/std", ] -hash-collections = ["wasmi_collections/hash-collections"] -prefer-btree-collections = ["wasmi_collections/prefer-btree-collections"] +hash-collections = [ + "wasmi_collections/hash-collections", + "wasmparser/hash-collections", +] +prefer-btree-collections = [ + "wasmi_collections/prefer-btree-collections", + "wasmparser/prefer-btree-collections", +] # Enables extra checks performed during Wasmi bytecode execution. # diff --git a/crates/wasmi/src/engine/config.rs b/crates/wasmi/src/engine/config.rs index 61a63b3f5f..aba5bd7df4 100644 --- a/crates/wasmi/src/engine/config.rs +++ b/crates/wasmi/src/engine/config.rs @@ -15,26 +15,8 @@ pub struct Config { stack_limits: StackLimits, /// The amount of Wasm stacks to keep in cache at most. cached_stacks: usize, - /// Is `true` if the `mutable-global` Wasm proposal is enabled. - mutable_global: bool, - /// Is `true` if the `sign-extension` Wasm proposal is enabled. - sign_extension: bool, - /// Is `true` if the `saturating-float-to-int` Wasm proposal is enabled. - saturating_float_to_int: bool, - /// Is `true` if the [`multi-value`] Wasm proposal is enabled. - multi_value: bool, - /// Is `true` if the [`multi-memory`] Wasm proposal is enabled. - multi_memory: bool, - /// Is `true` if the [`bulk-memory`] Wasm proposal is enabled. - bulk_memory: bool, - /// Is `true` if the [`reference-types`] Wasm proposal is enabled. - reference_types: bool, - /// Is `true` if the [`tail-call`] Wasm proposal is enabled. - tail_call: bool, - /// Is `true` if the [`extended-const`] Wasm proposal is enabled. - extended_const: bool, - /// Is `true` if Wasm instructions on `f32` and `f64` types are allowed. - floats: bool, + /// The Wasm features used when validating or translating functions. + features: WasmFeatures, /// Is `true` if Wasmi executions shall consume fuel. consume_fuel: bool, /// Is `true` if Wasmi shall ignore Wasm custom sections when parsing Wasm modules. @@ -174,16 +156,7 @@ impl Default for Config { Self { stack_limits: StackLimits::default(), cached_stacks: DEFAULT_CACHED_STACKS, - mutable_global: true, - sign_extension: true, - saturating_float_to_int: true, - multi_value: true, - multi_memory: true, - bulk_memory: true, - reference_types: true, - tail_call: true, - extended_const: true, - floats: true, + features: Self::default_features(), consume_fuel: false, ignore_custom_sections: false, fuel_costs: FuelCosts::default(), @@ -194,6 +167,23 @@ impl Default for Config { } impl Config { + /// Returns the default [`WasmFeatures`]. + fn default_features() -> WasmFeatures { + let mut features = WasmFeatures::empty(); + features.set(WasmFeatures::MUTABLE_GLOBAL, true); + features.set(WasmFeatures::MULTI_VALUE, true); + features.set(WasmFeatures::MULTI_MEMORY, true); + features.set(WasmFeatures::SATURATING_FLOAT_TO_INT, true); + features.set(WasmFeatures::SIGN_EXTENSION, true); + features.set(WasmFeatures::BULK_MEMORY, true); + features.set(WasmFeatures::REFERENCE_TYPES, true); + features.set(WasmFeatures::GC_TYPES, true); // required by reference-types + features.set(WasmFeatures::TAIL_CALL, true); + features.set(WasmFeatures::EXTENDED_CONST, true); + features.set(WasmFeatures::FLOATS, true); + features + } + /// Sets the [`StackLimits`] for the [`Config`]. pub fn set_stack_limits(&mut self, stack_limits: StackLimits) -> &mut Self { self.stack_limits = stack_limits; @@ -228,7 +218,7 @@ impl Config { /// /// [`mutable-global`]: https://github.com/WebAssembly/mutable-global pub fn wasm_mutable_global(&mut self, enable: bool) -> &mut Self { - self.mutable_global = enable; + self.features.set(WasmFeatures::MUTABLE_GLOBAL, enable); self } @@ -240,7 +230,7 @@ impl Config { /// /// [`sign-extension`]: https://github.com/WebAssembly/sign-extension-ops pub fn wasm_sign_extension(&mut self, enable: bool) -> &mut Self { - self.sign_extension = enable; + self.features.set(WasmFeatures::SIGN_EXTENSION, enable); self } @@ -253,7 +243,8 @@ impl Config { /// [`saturating-float-to-int`]: /// https://github.com/WebAssembly/nontrapping-float-to-int-conversions pub fn wasm_saturating_float_to_int(&mut self, enable: bool) -> &mut Self { - self.saturating_float_to_int = enable; + self.features + .set(WasmFeatures::SATURATING_FLOAT_TO_INT, enable); self } @@ -265,7 +256,7 @@ impl Config { /// /// [`multi-value`]: https://github.com/WebAssembly/multi-value pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self { - self.multi_value = enable; + self.features.set(WasmFeatures::MULTI_VALUE, enable); self } @@ -277,7 +268,7 @@ impl Config { /// /// [`multi-memory`]: https://github.com/WebAssembly/multi-memory pub fn wasm_multi_memory(&mut self, enable: bool) -> &mut Self { - self.multi_memory = enable; + self.features.set(WasmFeatures::MULTI_MEMORY, enable); self } @@ -289,7 +280,7 @@ impl Config { /// /// [`bulk-memory`]: https://github.com/WebAssembly/bulk-memory-operations pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self { - self.bulk_memory = enable; + self.features.set(WasmFeatures::BULK_MEMORY, enable); self } @@ -301,7 +292,8 @@ impl Config { /// /// [`reference-types`]: https://github.com/WebAssembly/reference-types pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self { - self.reference_types = enable; + self.features.set(WasmFeatures::REFERENCE_TYPES, enable); + self.features.set(WasmFeatures::GC_TYPES, enable); self } @@ -313,7 +305,7 @@ impl Config { /// /// [`tail-call`]: https://github.com/WebAssembly/tail-call pub fn wasm_tail_call(&mut self, enable: bool) -> &mut Self { - self.tail_call = enable; + self.features.set(WasmFeatures::TAIL_CALL, enable); self } @@ -325,7 +317,7 @@ impl Config { /// /// [`extended-const`]: https://github.com/WebAssembly/extended-const pub fn wasm_extended_const(&mut self, enable: bool) -> &mut Self { - self.extended_const = enable; + self.features.set(WasmFeatures::EXTENDED_CONST, enable); self } @@ -333,7 +325,7 @@ impl Config { /// /// Enabled by default. pub fn floats(&mut self, enable: bool) -> &mut Self { - self.floats = enable; + self.features.set(WasmFeatures::FLOATS, enable); self } @@ -420,24 +412,6 @@ impl Config { /// Returns the [`WasmFeatures`] represented by the [`Config`]. pub(crate) fn wasm_features(&self) -> WasmFeatures { - WasmFeatures { - multi_value: self.multi_value, - mutable_global: self.mutable_global, - saturating_float_to_int: self.saturating_float_to_int, - sign_extension: self.sign_extension, - bulk_memory: self.bulk_memory, - reference_types: self.reference_types, - tail_call: self.tail_call, - extended_const: self.extended_const, - floats: self.floats, - component_model: false, - simd: false, - relaxed_simd: false, - threads: false, - multi_memory: self.multi_memory, - exceptions: false, - memory64: false, - memory_control: false, - } + self.features } } diff --git a/crates/wasmi/src/engine/mod.rs b/crates/wasmi/src/engine/mod.rs index b7c24dd556..78031bec28 100644 --- a/crates/wasmi/src/engine/mod.rs +++ b/crates/wasmi/src/engine/mod.rs @@ -561,6 +561,7 @@ impl EngineInner { module: ModuleHeader, func_to_validate: Option>, ) -> Result<(), Error> { + let features = self.config().wasm_features(); match (self.config.get_compilation_mode(), func_to_validate) { (CompilationMode::Eager, Some(func_to_validate)) => { let (translation_allocs, validation_allocs) = self.get_allocs(); @@ -580,7 +581,8 @@ impl EngineInner { } (CompilationMode::LazyTranslation, Some(func_to_validate)) => { let allocs = self.get_validation_allocs(); - let translator = LazyFuncTranslator::new(func_index, engine_func, module, None); + let translator = + LazyFuncTranslator::new_unchecked(func_index, engine_func, module, features); let validator = func_to_validate.into_validator(allocs); let translator = ValidatingFuncTranslator::new(validator, translator)?; let allocs = FuncTranslationDriver::new(offset, bytes, translator)? @@ -588,8 +590,14 @@ impl EngineInner { self.recycle_validation_allocs(allocs.validation); } (CompilationMode::Lazy | CompilationMode::LazyTranslation, func_to_validate) => { - let translator = - LazyFuncTranslator::new(func_index, engine_func, module, func_to_validate); + let translator = match func_to_validate { + Some(func_to_validate) => { + LazyFuncTranslator::new(func_index, engine_func, module, func_to_validate) + } + None => { + LazyFuncTranslator::new_unchecked(func_index, engine_func, module, features) + } + }; FuncTranslationDriver::new(offset, bytes, translator)? .translate(|func_entity| self.init_func(engine_func, func_entity))?; } diff --git a/crates/wasmi/src/engine/translator/driver.rs b/crates/wasmi/src/engine/translator/driver.rs index 4dc0256bc2..75062be1f8 100644 --- a/crates/wasmi/src/engine/translator/driver.rs +++ b/crates/wasmi/src/engine/translator/driver.rs @@ -2,7 +2,7 @@ use crate::{ engine::{code_map::CompiledFuncEntity, WasmTranslator}, Error, }; -use wasmparser::FunctionBody; +use wasmparser::{BinaryReader, FunctionBody}; /// Translates Wasm bytecode into Wasmi bytecode for a single Wasm function. pub struct FuncTranslationDriver<'parser, T> { @@ -14,7 +14,10 @@ pub struct FuncTranslationDriver<'parser, T> { translator: T, } -impl<'parser, T> FuncTranslationDriver<'parser, T> { +impl<'parser, T> FuncTranslationDriver<'parser, T> +where + T: WasmTranslator<'parser>, +{ /// Creates a new Wasm to Wasmi bytecode function translator. pub fn new( offset: impl Into>, @@ -22,19 +25,16 @@ impl<'parser, T> FuncTranslationDriver<'parser, T> { translator: T, ) -> Result { let offset = offset.into().unwrap_or(0); - let func_body = FunctionBody::new(offset, bytes); + let features = translator.features(); + let reader = BinaryReader::new_features(bytes, offset, features); + let func_body = FunctionBody::new(reader); Ok(Self { func_body, bytes, translator, }) } -} -impl<'parser, T> FuncTranslationDriver<'parser, T> -where - T: WasmTranslator<'parser>, -{ /// Starts translation of the Wasm stream into Wasmi bytecode. pub fn translate( mut self, diff --git a/crates/wasmi/src/engine/translator/mod.rs b/crates/wasmi/src/engine/translator/mod.rs index d69a6e0a09..0187260e73 100644 --- a/crates/wasmi/src/engine/translator/mod.rs +++ b/crates/wasmi/src/engine/translator/mod.rs @@ -66,7 +66,7 @@ use crate::{ FuncType, }; use alloc::vec::Vec; -use core::fmt; +use core::{fmt, mem}; use stack::RegisterSpace; use utils::Wrap; use wasmparser::{ @@ -76,6 +76,7 @@ use wasmparser::{ MemArg, ValidatorResources, VisitOperator, + WasmFeatures, }; macro_rules! impl_typed_for { @@ -207,6 +208,9 @@ pub trait WasmTranslator<'parser>: VisitOperator<'parser, Output = Result<(), Er /// used for translation of the Wasm function body. fn setup(&mut self, bytes: &[u8]) -> Result; + /// Returns a reference to the [`WasmFeatures`] used by the [`WasmTranslator`]. + fn features(&self) -> WasmFeatures; + /// Translates the given local variables for the translated function. fn translate_locals( &mut self, @@ -290,6 +294,10 @@ where Ok(false) } + fn features(&self) -> WasmFeatures { + self.translator.features() + } + fn translate_locals( &mut self, amount: u32, @@ -327,7 +335,7 @@ where } macro_rules! impl_visit_operator { - ( @mvp BrTable { $arg:ident: $argty:ty } => $visit:ident $($rest:tt)* ) => { + ( @mvp BrTable { $arg:ident: $argty:ty } => $visit:ident $_ann:tt $($rest:tt)* ) => { // We need to special case the `BrTable` operand since its // arguments (a.k.a. `BrTable<'a>`) are not `Copy` which all // the other impls make use of. @@ -358,7 +366,7 @@ macro_rules! impl_visit_operator { ( @tail_call $($rest:tt)* ) => { impl_visit_operator!(@@supported $($rest)*); }; - ( @@supported $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + ( @@supported $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $_ann:tt $($rest:tt)* ) => { fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { let offset = self.current_pos(); self.validate_then_translate( @@ -368,7 +376,7 @@ macro_rules! impl_visit_operator { } impl_visit_operator!($($rest)*); }; - ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt $($rest:tt)* ) => { // Wildcard match arm for all the other (yet) unsupported Wasm proposals. fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output { let offset = self.current_pos(); @@ -385,10 +393,11 @@ where { type Output = Result<(), Error>; - wasmparser::for_each_operator!(impl_visit_operator); + wasmparser::for_each_visit_operator!(impl_visit_operator); } /// A lazy Wasm function translator that defers translation when the function is first used. +#[derive(Debug)] pub struct LazyFuncTranslator { /// The index of the lazily compiled function within its module. func_idx: FuncIdx, @@ -396,17 +405,51 @@ pub struct LazyFuncTranslator { engine_func: EngineFunc, /// The Wasm module header information used for translation. module: ModuleHeader, - /// Optional information about lazy Wasm validation. - func_to_validate: Option>, + /// Information about Wasm validation during lazy translation. + validation: Validation, +} + +/// Information about Wasm validation for lazy translation. +enum Validation { + /// Wasm validation is performed. + Checked(FuncToValidate), + /// Wasm validation is checked. + /// + /// # Dev. Note + /// + /// We still need Wasm features to properly parse the Wasm. + Unchecked(WasmFeatures), +} + +impl Validation { + /// Returns `true` if `self` performs validates Wasm upon lazy translation. + pub fn is_checked(&self) -> bool { + matches!(self, Self::Checked(_)) + } + + /// Returns the [`WasmFeatures`] used for Wasm parsing and validation. + pub fn features(&self) -> WasmFeatures { + match self { + Validation::Checked(func_to_validate) => func_to_validate.features, + Validation::Unchecked(wasm_features) => *wasm_features, + } + } + + /// Returns the [`FuncToValidate`] if `self` is checked. + pub fn take_func_to_validate(&mut self) -> Option> { + let features = self.features(); + match mem::replace(self, Self::Unchecked(features)) { + Self::Checked(func_to_validate) => Some(func_to_validate), + Self::Unchecked(_) => None, + } + } } -impl fmt::Debug for LazyFuncTranslator { +impl fmt::Debug for Validation { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("LazyFuncTranslator") - .field("func_idx", &self.func_idx) - .field("engine_func", &self.engine_func) - .field("module", &self.module) - .field("validate", &self.func_to_validate.is_some()) + .field("validate", &self.is_checked()) + .field("features", &self.features()) .finish() } } @@ -417,13 +460,28 @@ impl LazyFuncTranslator { func_idx: FuncIdx, engine_func: EngineFunc, module: ModuleHeader, - func_to_validate: Option>, + func_to_validate: FuncToValidate, ) -> Self { Self { func_idx, engine_func, module, - func_to_validate, + validation: Validation::Checked(func_to_validate), + } + } + + /// Create a new [`LazyFuncTranslator`] that does not validate Wasm upon lazy translation. + pub fn new_unchecked( + func_idx: FuncIdx, + engine_func: EngineFunc, + module: ModuleHeader, + features: WasmFeatures, + ) -> Self { + Self { + func_idx, + engine_func, + module, + validation: Validation::Unchecked(features), } } } @@ -446,11 +504,16 @@ impl WasmTranslator<'_> for LazyFuncTranslator { self.engine_func, bytes, &self.module, - self.func_to_validate.take(), + self.validation.take_func_to_validate(), ); Ok(true) } + #[inline] + fn features(&self) -> WasmFeatures { + self.validation.features() + } + #[inline] fn translate_locals( &mut self, @@ -478,7 +541,7 @@ impl WasmTranslator<'_> for LazyFuncTranslator { } macro_rules! impl_visit_operator { - ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt $($rest:tt)* ) => { #[inline] fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output { Ok(()) @@ -491,7 +554,7 @@ macro_rules! impl_visit_operator { impl<'a> VisitOperator<'a> for LazyFuncTranslator { type Output = Result<(), Error>; - wasmparser::for_each_operator!(impl_visit_operator); + wasmparser::for_each_visit_operator!(impl_visit_operator); } /// Type concerned with translating from Wasm bytecode to Wasmi bytecode. @@ -533,6 +596,11 @@ impl WasmTranslator<'_> for FuncTranslator { Ok(false) } + #[inline] + fn features(&self) -> WasmFeatures { + self.engine.config().wasm_features() + } + fn translate_locals( &mut self, amount: u32, diff --git a/crates/wasmi/src/engine/translator/visit.rs b/crates/wasmi/src/engine/translator/visit.rs index 621d9035ef..7362808bb9 100644 --- a/crates/wasmi/src/engine/translator/visit.rs +++ b/crates/wasmi/src/engine/translator/visit.rs @@ -57,11 +57,11 @@ macro_rules! impl_visit_operator { ( @tail_call $($rest:tt)* ) => { impl_visit_operator!(@@skipped $($rest)*); }; - ( @@skipped $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + ( @@skipped $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $_ann:tt $($rest:tt)* ) => { // We skip Wasm operators that we already implement manually. impl_visit_operator!($($rest)*); }; - ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $($rest:tt)* ) => { + ( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $_ann:tt $($rest:tt)* ) => { // Wildcard match arm for all the other (yet) unsupported Wasm proposals. fn $visit(&mut self $($(, $arg: $argty)*)?) -> Self::Output { self.unsupported_operator(stringify!($op)) @@ -88,7 +88,7 @@ impl FuncTranslator { impl<'a> VisitOperator<'a> for FuncTranslator { type Output = Result<(), Error>; - wasmparser::for_each_operator!(impl_visit_operator); + wasmparser::for_each_visit_operator!(impl_visit_operator); fn visit_unreachable(&mut self) -> Self::Output { bail_unreachable!(self); @@ -511,12 +511,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { Ok(()) } - fn visit_call_indirect( - &mut self, - type_index: u32, - table_index: u32, - _table_byte: u8, - ) -> Self::Output { + fn visit_call_indirect(&mut self, type_index: u32, table_index: u32) -> Self::Output { bail_unreachable!(self); self.bump_fuel_consumption(FuelCosts::call)?; let type_index = FuncType::from(type_index); @@ -979,7 +974,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { ) } - fn visit_memory_size(&mut self, mem: u32, _mem_byte: u8) -> Self::Output { + fn visit_memory_size(&mut self, mem: u32) -> Self::Output { bail_unreachable!(self); let memory = index::Memory::from(mem); let result = self.alloc.stack.push_dynamic()?; @@ -987,7 +982,7 @@ impl<'a> VisitOperator<'a> for FuncTranslator { Ok(()) } - fn visit_memory_grow(&mut self, mem: u32, _mem_byte: u8) -> Self::Output { + fn visit_memory_grow(&mut self, mem: u32) -> Self::Output { bail_unreachable!(self); let delta = self.alloc.stack.pop().map_const(u32::from); let memory = index::Memory::from(mem); @@ -1036,9 +1031,9 @@ impl<'a> VisitOperator<'a> for FuncTranslator { Ok(()) } - fn visit_ref_null(&mut self, ty: wasmparser::ValType) -> Self::Output { + fn visit_ref_null(&mut self, hty: wasmparser::HeapType) -> Self::Output { bail_unreachable!(self); - let type_hint = WasmiValueType::from(ty).into_inner(); + let type_hint = WasmiValueType::from(hty).into_inner(); let null = match type_hint { ValType::FuncRef => TypedVal::from(FuncRef::null()), ValType::ExternRef => TypedVal::from(ExternRef::null()), diff --git a/crates/wasmi/src/lib.rs b/crates/wasmi/src/lib.rs index ebc0f4c8d8..f1d7b1d98e 100644 --- a/crates/wasmi/src/lib.rs +++ b/crates/wasmi/src/lib.rs @@ -77,7 +77,7 @@ clippy::default_trait_access, clippy::items_after_statements )] -#![recursion_limit = "750"] +#![recursion_limit = "1000"] extern crate alloc; #[cfg(feature = "std")] diff --git a/crates/wasmi/src/module/element.rs b/crates/wasmi/src/module/element.rs index 2b7928340e..d87322ab63 100644 --- a/crates/wasmi/src/module/element.rs +++ b/crates/wasmi/src/module/element.rs @@ -1,5 +1,5 @@ use super::{ConstExpr, TableIdx}; -use crate::{core::ValType, module::utils::WasmiValueType}; +use crate::core::ValType; use alloc::boxed::Box; /// A table element segment within a [`Module`]. @@ -54,7 +54,7 @@ impl From> for ElementSegmentKind { table_index, offset_expr, } => { - let table_index = TableIdx::from(table_index); + let table_index = TableIdx::from(table_index.unwrap_or(0)); let offset = ConstExpr::new(offset_expr); Self::Active(ActiveElementSegment { table_index, @@ -69,28 +69,33 @@ impl From> for ElementSegmentKind { impl From> for ElementSegment { fn from(element: wasmparser::Element<'_>) -> Self { - assert!( - element.ty.is_reference_type(), - "only reftypes are allowed as element types but found: {:?}", - element.ty - ); let kind = ElementSegmentKind::from(element.kind); - let ty = WasmiValueType::from(element.ty).into_inner(); - let items = match element.items { - wasmparser::ElementItems::Functions(items) => items - .into_iter() - .map(|item| { - item.unwrap_or_else(|error| panic!("failed to parse element item: {error}")) - }) - .map(ConstExpr::new_funcref) - .collect::>(), - wasmparser::ElementItems::Expressions(items) => items - .into_iter() - .map(|item| { - item.unwrap_or_else(|error| panic!("failed to parse element item: {error}")) - }) - .map(ConstExpr::new) - .collect::>(), + let (items, ty) = match element.items { + wasmparser::ElementItems::Functions(items) => { + let items = items + .into_iter() + .map(|item| { + item.unwrap_or_else(|error| panic!("failed to parse element item: {error}")) + }) + .map(ConstExpr::new_funcref) + .collect::>(); + (items, ValType::FuncRef) + } + wasmparser::ElementItems::Expressions(ref_ty, items) => { + let ty = match ref_ty { + ty if ty.is_func_ref() => ValType::FuncRef, + ty if ty.is_extern_ref() => ValType::ExternRef, + _ => panic!("unsupported Wasm reference type"), + }; + let items = items + .into_iter() + .map(|item| { + item.unwrap_or_else(|error| panic!("failed to parse element item: {error}")) + }) + .map(ConstExpr::new) + .collect::>(); + (items, ty) + } }; Self { kind, ty, items } } diff --git a/crates/wasmi/src/module/init_expr.rs b/crates/wasmi/src/module/init_expr.rs index 11c418440c..3a2d1ec442 100644 --- a/crates/wasmi/src/module/init_expr.rs +++ b/crates/wasmi/src/module/init_expr.rs @@ -16,6 +16,7 @@ use crate::{ use alloc::boxed::Box; use core::fmt; use smallvec::SmallVec; +use wasmparser::AbstractHeapType; /// Types that allow evluation given an evaluation context. pub trait Eval { @@ -267,11 +268,19 @@ impl ConstExpr { wasmparser::Operator::GlobalGet { global_index } => { stack.push(Op::global(global_index)); } - wasmparser::Operator::RefNull { ty } => { - let value = match ty { - wasmparser::ValType::FuncRef => Val::from(FuncRef::null()), - wasmparser::ValType::ExternRef => Val::from(ExternRef::null()), - ty => panic!("encountered invalid value type for RefNull: {ty:?}"), + wasmparser::Operator::RefNull { hty } => { + let value = match hty { + wasmparser::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Func, + } => Val::from(FuncRef::null()), + wasmparser::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Extern, + } => Val::from(ExternRef::null()), + invalid => { + panic!("encountered invalid heap type for `ref.null`: {invalid:?}") + } }; stack.push(Op::constant(value)); } diff --git a/crates/wasmi/src/module/parser.rs b/crates/wasmi/src/module/parser.rs index 42282be296..20093e52d9 100644 --- a/crates/wasmi/src/module/parser.rs +++ b/crates/wasmi/src/module/parser.rs @@ -113,20 +113,21 @@ impl ModuleParser { } let limits = self.engine.config().get_enforced_limits(); let func_types = section.into_iter().map(|result| { - let wasmparser::Type::Func(ty) = result?; + let ty = result?.into_types().next().unwrap(); + let func_ty = ty.unwrap_func(); if let Some(limit) = limits.max_params { - if ty.params().len() > limit { + if func_ty.params().len() > limit { return Err(Error::from(EnforcedLimitsError::TooManyParameters { limit, })); } } if let Some(limit) = limits.max_results { - if ty.results().len() > limit { + if func_ty.results().len() > limit { return Err(Error::from(EnforcedLimitsError::TooManyResults { limit })); } } - Ok(FuncType::from_wasmparser(ty)) + Ok(FuncType::from_wasmparser(func_ty)) }); header.push_func_types(func_types)?; Ok(()) @@ -208,9 +209,13 @@ impl ModuleParser { if let Some(validator) = &mut self.validator { validator.table_section(§ion)?; } - let tables = section - .into_iter() - .map(|table| table.map(TableType::from_wasmparser).map_err(Error::from)); + let tables = section.into_iter().map(|table| match table { + Ok(table) => { + assert!(matches!(table.init, wasmparser::TableInit::RefNull)); + Ok(TableType::from_wasmparser(table.ty)) + } + Err(err) => Err(err.into()), + }); header.push_tables(tables)?; Ok(()) } diff --git a/crates/wasmi/src/module/parser/buffered.rs b/crates/wasmi/src/module/parser/buffered.rs index bb3d145ea1..8217ec3854 100644 --- a/crates/wasmi/src/module/parser/buffered.rs +++ b/crates/wasmi/src/module/parser/buffered.rs @@ -162,10 +162,8 @@ impl ModuleParser { // to return the byte slice for the respective code section // entry payload. Please remove this work around as soon as // such an API becomes available. - let bytes = Self::consume_buffer(consumed, buffer); - let remaining = func_body.get_binary_reader().bytes_remaining(); - let start = consumed - remaining; - let bytes = &bytes[start..]; + Self::consume_buffer(consumed, buffer); + let bytes = func_body.as_bytes(); self.process_code_entry(func_body, bytes, &header)?; } _ => break, diff --git a/crates/wasmi/src/module/parser/streaming.rs b/crates/wasmi/src/module/parser/streaming.rs index 8caf08de39..e623f851e2 100644 --- a/crates/wasmi/src/module/parser/streaming.rs +++ b/crates/wasmi/src/module/parser/streaming.rs @@ -237,9 +237,7 @@ impl ModuleParser { // to return the byte slice for the respective code section // entry payload. Please remove this work around as soon as // such an API becomes available. - let remaining = func_body.get_binary_reader().bytes_remaining(); - let start = consumed - remaining; - let bytes = &buffer[start..consumed]; + let bytes = func_body.as_bytes(); self.process_code_entry(func_body, bytes, &header)?; } _ => break, diff --git a/crates/wasmi/src/module/utils.rs b/crates/wasmi/src/module/utils.rs index be13ecdb06..68b771dccf 100644 --- a/crates/wasmi/src/module/utils.rs +++ b/crates/wasmi/src/module/utils.rs @@ -1,3 +1,5 @@ +use wasmparser::AbstractHeapType; + use crate::{core::ValType, FuncType, GlobalType, MemoryType, Mutability, TableType}; impl TableType { @@ -9,8 +11,19 @@ impl TableType { /// routine does not become part of the public API of [`TableType`]. pub(crate) fn from_wasmparser(table_type: wasmparser::TableType) -> Self { let element = WasmiValueType::from(table_type.element_type).into_inner(); - let minimum = table_type.initial; - let maximum = table_type.maximum; + let minimum: u32 = table_type + .initial + .try_into() + .unwrap_or_else(|_err| panic!("out of bounds minimum value: {}", table_type.initial)); + let maximum: Option = match table_type.maximum { + Some(maximum) => { + let maximum = maximum + .try_into() + .unwrap_or_else(|_err| panic!("out of bounds maximum value: {}", maximum)); + Some(maximum) + } + None => None, + }; Self::new(element, minimum, maximum) } } @@ -69,7 +82,7 @@ impl FuncType { /// /// We do not use the `From` trait here so that this conversion /// routine does not become part of the public API of [`FuncType`]. - pub(crate) fn from_wasmparser(func_type: wasmparser::FuncType) -> Self { + pub(crate) fn from_wasmparser(func_type: &wasmparser::FuncType) -> Self { /// Returns the [`ValType`] from the given [`wasmparser::Type`]. /// /// # Panics @@ -106,6 +119,32 @@ impl From for WasmiValueType { } } +impl From for WasmiValueType { + fn from(heap_type: wasmparser::HeapType) -> Self { + match heap_type { + wasmparser::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Func, + } => Self::from(ValType::FuncRef), + wasmparser::HeapType::Abstract { + shared: false, + ty: AbstractHeapType::Extern, + } => Self::from(ValType::ExternRef), + unsupported => panic!("encountered unsupported heap type: {unsupported:?}"), + } + } +} + +impl From for WasmiValueType { + fn from(ref_type: wasmparser::RefType) -> Self { + match ref_type { + wasmparser::RefType::FUNCREF => Self::from(ValType::FuncRef), + wasmparser::RefType::EXTERNREF => Self::from(ValType::ExternRef), + unsupported => panic!("encountered unsupported reference type: {unsupported:?}"), + } + } +} + impl From for WasmiValueType { fn from(value_type: wasmparser::ValType) -> Self { match value_type { @@ -114,8 +153,7 @@ impl From for WasmiValueType { wasmparser::ValType::F32 => Self::from(ValType::F32), wasmparser::ValType::F64 => Self::from(ValType::F64), wasmparser::ValType::V128 => panic!("wasmi does not support the `simd` Wasm proposal"), - wasmparser::ValType::FuncRef => Self::from(ValType::FuncRef), - wasmparser::ValType::ExternRef => Self::from(ValType::ExternRef), + wasmparser::ValType::Ref(ref_type) => WasmiValueType::from(ref_type), } } } diff --git a/crates/wasmi/src/table/element.rs b/crates/wasmi/src/table/element.rs index 596e7bd4f4..41de1b2bf5 100644 --- a/crates/wasmi/src/table/element.rs +++ b/crates/wasmi/src/table/element.rs @@ -107,11 +107,7 @@ impl ElementSegmentEntity { panic!("unexpected failed initialization of constant expression: {const_expr:?}") }) }).collect::>(); - Self { - ty, - // items: Some(elem.items_cloned()), - items, - } + Self { ty, items } } module::ElementSegmentKind::Declared => Self::empty(ty), }