diff --git a/Cargo.lock b/Cargo.lock index 2165f2188b8..6741f3fb143 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5709,6 +5709,7 @@ dependencies = [ name = "wasmer-types" version = "3.2.0-beta.1" dependencies = [ + "bytecheck", "enum-iterator", "enumset", "indexmap", diff --git a/lib/api/src/js/module.rs b/lib/api/src/js/module.rs index 995161a9839..fabcc8f5923 100644 --- a/lib/api/src/js/module.rs +++ b/lib/api/src/js/module.rs @@ -238,6 +238,18 @@ impl Module { return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string())); } + pub fn deserialize_checked( + _engine: &impl AsEngineRef, + _bytes: impl IntoBytes, + ) -> Result { + #[cfg(feature = "js-serializable-module")] + return Self::from_binary(_engine, &_bytes.into_bytes()) + .map_err(|e| DeserializeError::Compiler(e)); + + #[cfg(not(feature = "js-serializable-module"))] + return Err(DeserializeError::Generic("You need to enable the `js-serializable-module` feature flag to deserialize a `Module`".to_string())); + } + pub unsafe fn deserialize_from_file( engine: &impl AsEngineRef, path: impl AsRef, @@ -246,6 +258,14 @@ impl Module { Self::deserialize(engine, bytes) } + pub fn deserialize_from_file_checked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let bytes = std::fs::read(path.as_ref())?; + Self::deserialize_checked(engine, bytes) + } + pub fn set_name(&mut self, name: &str) -> bool { self.name = Some(name.to_string()); true diff --git a/lib/api/src/module.rs b/lib/api/src/module.rs index a7e79c06aea..41eb074d5e5 100644 --- a/lib/api/src/module.rs +++ b/lib/api/src/module.rs @@ -219,6 +219,8 @@ impl Module { /// Deserializes a serialized Module binary into a `Module`. /// + /// Note: You should usually prefer the safe [`Module::deserialize_checked`]. + /// /// # Important /// /// This function only accepts a custom binary format, which will be different @@ -253,6 +255,56 @@ impl Module { Ok(Self(module_imp::Module::deserialize(engine, bytes)?)) } + /// Deserializes a serialized Module binary into a `Module`. + /// + /// # Important + /// + /// This function only accepts a custom binary format, which will be different + /// than the `wasm` binary format and may change among Wasmer versions. + /// (it should be the result of the serialization of a Module via the + /// `Module::serialize` method.). + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # fn main() -> anyhow::Result<()> { + /// # let mut store = Store::default(); + /// let module = Module::deserialize_checked(&store, serialized_data)?; + /// # Ok(()) + /// # } + /// ``` + pub fn deserialize_checked( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + Ok(Self(module_imp::Module::deserialize_checked( + engine, bytes, + )?)) + } + + /// Deserializes a a serialized Module located in a `Path` into a `Module`. + /// > Note: the module has to be serialized before with the `serialize` method. + /// + /// # Usage + /// + /// ```ignore + /// # use wasmer::*; + /// # let mut store = Store::default(); + /// # fn main() -> anyhow::Result<()> { + /// let module = Module::deserialize_from_file(&store, path)?; + /// # Ok(()) + /// # } + /// ``` + pub fn deserialize_from_file_checked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + Ok(Self(module_imp::Module::deserialize_from_file_checked( + engine, path, + )?)) + } + /// Deserializes a a serialized Module located in a `Path` into a `Module`. /// > Note: the module has to be serialized before with the `serialize` method. /// diff --git a/lib/api/src/sys/module.rs b/lib/api/src/sys/module.rs index b3a67a9ddd5..108dbe11e92 100644 --- a/lib/api/src/sys/module.rs +++ b/lib/api/src/sys/module.rs @@ -78,6 +78,19 @@ impl Module { Ok(Self::from_artifact(artifact)) } + pub fn deserialize_checked( + engine: &impl AsEngineRef, + bytes: impl IntoBytes, + ) -> Result { + let bytes = bytes.into_bytes(); + let artifact = engine + .as_engine_ref() + .engine() + .0 + .deserialize_checked(&bytes)?; + Ok(Self::from_artifact(artifact)) + } + pub unsafe fn deserialize_from_file( engine: &impl AsEngineRef, path: impl AsRef, @@ -90,6 +103,18 @@ impl Module { Ok(Self::from_artifact(artifact)) } + pub fn deserialize_from_file_checked( + engine: &impl AsEngineRef, + path: impl AsRef, + ) -> Result { + let artifact = engine + .as_engine_ref() + .engine() + .0 + .deserialize_from_file_checked(path.as_ref())?; + Ok(Self::from_artifact(artifact)) + } + fn from_artifact(artifact: Arc) -> Self { Self { artifact } } diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 7a795ef86ef..a8027e7e399 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -427,7 +427,7 @@ impl RunWithPathBuf { if wasmer_compiler::Artifact::is_deserializable(&contents) { let engine = wasmer_compiler::EngineBuilder::headless(); let store = Store::new(engine); - let module = unsafe { Module::deserialize_from_file(&store, &self.path)? }; + let module = Module::deserialize_from_file_checked(&store, &self.path)?; return Ok((store, module)); } let (store, compiler_type) = self.store.get_store()?; diff --git a/lib/compiler/src/engine/artifact.rs b/lib/compiler/src/engine/artifact.rs index 6c20b397686..78bcbc1a845 100644 --- a/lib/compiler/src/engine/artifact.rs +++ b/lib/compiler/src/engine/artifact.rs @@ -148,6 +148,30 @@ impl Artifact { /// # Safety /// This function is unsafe because rkyv reads directly without validating /// the data. + pub fn deserialize_checked(engine: &Engine, bytes: &[u8]) -> Result { + if !ArtifactBuild::is_deserializable(bytes) { + return Err(DeserializeError::Incompatible( + "Magic header not found".to_string(), + )); + } + + let bytes = Self::get_byte_slice(bytes, ArtifactBuild::MAGIC_HEADER.len(), bytes.len())?; + + let metadata_len = MetadataHeader::parse(bytes)?; + let metadata_slice = Self::get_byte_slice(bytes, MetadataHeader::LEN, bytes.len())?; + let metadata_slice = Self::get_byte_slice(metadata_slice, 0, metadata_len)?; + + let serializable = SerializableModule::deserialize_checked(metadata_slice)?; + let artifact = ArtifactBuild::from_serializable(serializable); + let mut inner_engine = engine.inner_mut(); + Self::from_parts(&mut inner_engine, artifact, engine.target()) + .map_err(DeserializeError::Compiler) + } + + /// Deserialize a ArtifactBuild + /// + /// # Safety + /// This function is unsafe because rkyv reads directly without validating the data. pub unsafe fn deserialize(engine: &Engine, bytes: &[u8]) -> Result { if !ArtifactBuild::is_deserializable(bytes) { let static_artifact = Self::deserialize_object(engine, bytes); diff --git a/lib/compiler/src/engine/inner.rs b/lib/compiler/src/engine/inner.rs index 378d80daa9e..3b89c1c7662 100644 --- a/lib/compiler/src/engine/inner.rs +++ b/lib/compiler/src/engine/inner.rs @@ -200,12 +200,28 @@ impl Engine { Ok(Arc::new(Artifact::deserialize(self, bytes)?)) } + /// Deserializes a WebAssembly module #[cfg(not(target_arch = "wasm32"))] + pub fn deserialize_checked(&self, bytes: &[u8]) -> Result, DeserializeError> { + Ok(Arc::new(Artifact::deserialize_checked(self, bytes)?)) + } + /// Deserializes a WebAssembly module from a path + #[cfg(not(target_arch = "wasm32"))] + pub fn deserialize_from_file_checked( + &self, + file_ref: &Path, + ) -> Result, DeserializeError> { + let contents = std::fs::read(file_ref)?; + self.deserialize_checked(&contents) + } + + /// Deserialize from a file path. /// /// # Safety /// - /// The file's content must represent a serialized WebAssembly module. + /// See [`crate::Module::deserialize_from_file`]. + #[cfg(not(target_arch = "wasm32"))] pub unsafe fn deserialize_from_file( &self, file_ref: &Path, diff --git a/lib/types/Cargo.toml b/lib/types/Cargo.toml index f7afbcf022d..62d8cea7f33 100644 --- a/lib/types/Cargo.toml +++ b/lib/types/Cargo.toml @@ -16,10 +16,11 @@ serde_bytes = { version = "0.11", optional = true } thiserror = "1.0" more-asserts = "0.2" indexmap = { version = "1.6" } -rkyv = { version = "0.7.40", features = ["indexmap"] } +rkyv = { version = "0.7.40", features = ["indexmap", "validation", "strict"] } enum-iterator = "0.7.0" target-lexicon = { version = "0.12.2", default-features = false } enumset = "1.0" +bytecheck = "0.6.8" [dev-dependencies] memoffset = "0.6" diff --git a/lib/types/src/compilation/address_map.rs b/lib/types/src/compilation/address_map.rs index 6d7c1d973d9..e9db7f467cd 100644 --- a/lib/types/src/compilation/address_map.rs +++ b/lib/types/src/compilation/address_map.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; /// Single source location to generated address mapping. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct InstructionAddressMap { /// Original source location. pub srcloc: SourceLoc, @@ -24,6 +25,7 @@ pub struct InstructionAddressMap { /// Function and its instructions addresses mappings. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct FunctionAddressMap { /// Instructions maps. /// The array is sorted by the InstructionAddressMap::code_offset field. diff --git a/lib/types/src/compilation/function.rs b/lib/types/src/compilation/function.rs index e87d15c59ed..2becb5c4652 100644 --- a/lib/types/src/compilation/function.rs +++ b/lib/types/src/compilation/function.rs @@ -21,6 +21,7 @@ use serde::{Deserialize, Serialize}; /// the frame information after a `Trap`. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct CompiledFunctionFrameInfo { /// The traps (in the function body). /// @@ -34,6 +35,7 @@ pub struct CompiledFunctionFrameInfo { /// The function body. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct FunctionBody { /// The function body bytes. #[cfg_attr(feature = "enable-serde", serde(with = "serde_bytes"))] @@ -50,6 +52,7 @@ pub struct FunctionBody { /// and unwind information). #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct CompiledFunction { /// The function body. pub body: FunctionBody, @@ -74,7 +77,9 @@ pub type CustomSections = PrimaryMap; /// In the future this structure may also hold other information useful /// for debugging. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, PartialEq, Eq, Clone)] +#[derive( + RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, PartialEq, Eq, Clone, +)] #[archive(as = "Self")] pub struct Dwarf { /// The section index in the [`Compilation`] that corresponds to the exception frames. diff --git a/lib/types/src/compilation/module.rs b/lib/types/src/compilation/module.rs index 9869e6cfce3..8c9c460d931 100644 --- a/lib/types/src/compilation/module.rs +++ b/lib/types/src/compilation/module.rs @@ -13,6 +13,7 @@ use std::sync::Arc; /// or the `MemoryStyle` and `TableStyle`). #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] #[derive(Debug, Clone, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct CompileModuleInfo { /// The features used for compiling the module pub features: Features, diff --git a/lib/types/src/compilation/relocation.rs b/lib/types/src/compilation/relocation.rs index b1a18e42da0..ce51cf9b88c 100644 --- a/lib/types/src/compilation/relocation.rs +++ b/lib/types/src/compilation/relocation.rs @@ -21,8 +21,11 @@ use serde::{Deserialize, Serialize}; /// Relocation kinds for every ISA. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[derive(RkyvSerialize, RkyvDeserialize, Archive, Copy, Clone, Debug, PartialEq, Eq)] +#[derive( + RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Copy, Clone, Debug, PartialEq, Eq, +)] #[archive(as = "Self")] +#[repr(u8)] pub enum RelocationKind { /// absolute 4-byte Abs4, @@ -90,6 +93,7 @@ impl fmt::Display for RelocationKind { /// A record of a relocation to perform. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct Relocation { /// The relocation kind. pub kind: RelocationKind, @@ -103,8 +107,11 @@ pub struct Relocation { /// Destination function. Can be either user function or some special one, like `memory.grow`. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq)] +#[derive( + RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, Copy, Clone, PartialEq, Eq, +)] #[archive(as = "Self")] +#[repr(u8)] pub enum RelocationTarget { /// A relocation to a function defined locally in the wasm (not an imported one). LocalFunc(LocalFunctionIndex), diff --git a/lib/types/src/compilation/section.rs b/lib/types/src/compilation/section.rs index 5431ca47895..8a593d7a07e 100644 --- a/lib/types/src/compilation/section.rs +++ b/lib/types/src/compilation/section.rs @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize}; RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, Copy, Clone, PartialEq, @@ -37,8 +38,11 @@ entity_impl!(SectionIndex); /// /// Determines how a custom section may be used. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] +#[derive( + RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, Clone, PartialEq, Eq, +)] #[archive(as = "Self")] +#[repr(u8)] pub enum CustomSectionProtection { /// A custom section with read permission. Read, @@ -53,6 +57,7 @@ pub enum CustomSectionProtection { /// in the emitted module. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct CustomSection { /// Memory protection that applies to this section. pub protection: CustomSectionProtection, @@ -72,6 +77,7 @@ pub struct CustomSection { /// The bytes in the section. #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq, Default)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct SectionBody(#[cfg_attr(feature = "enable-serde", serde(with = "serde_bytes"))] Vec); impl SectionBody { diff --git a/lib/types/src/compilation/sourceloc.rs b/lib/types/src/compilation/sourceloc.rs index 075f3724d8e..1ddb3275a05 100644 --- a/lib/types/src/compilation/sourceloc.rs +++ b/lib/types/src/compilation/sourceloc.rs @@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize}; )] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] #[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, rkyv::CheckBytes)] #[archive(as = "Self")] pub struct SourceLoc(u32); diff --git a/lib/types/src/compilation/symbols.rs b/lib/types/src/compilation/symbols.rs index 81bd8d5ceb7..8cfbe50d937 100644 --- a/lib/types/src/compilation/symbols.rs +++ b/lib/types/src/compilation/symbols.rs @@ -5,9 +5,9 @@ use crate::{ SectionIndex, SerializeError, SignatureIndex, }; use rkyv::{ - archived_value, de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer, - ser::Serializer as RkyvSerializer, Archive, Deserialize as RkyvDeserialize, - Serialize as RkyvSerialize, + archived_value, check_archived_value, de::deserializers::SharedDeserializeMap, + ser::serializers::AllocSerializer, ser::Serializer as RkyvSerializer, Archive, + Deserialize as RkyvDeserialize, Serialize as RkyvSerialize, }; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -50,6 +50,7 @@ pub trait SymbolRegistry: Send + Sync { /// Serializable struct that represents the compiled metadata. #[derive(Debug, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct ModuleMetadata { /// Compile info pub compile_info: CompileModuleInfo, @@ -114,6 +115,14 @@ impl ModuleMetadata { Self::deserialize_from_archive(archived) } + /// Deserialize a Module from a slice. + /// The slice must have the following format: + /// RKYV serialization (any length) + POS (8 bytes) + pub fn deserialize_checked(metadata_slice: &[u8]) -> Result { + let archived = Self::archive_from_slice_checked(metadata_slice)?; + Self::deserialize_from_archive(archived) + } + /// # Safety /// /// This method is unsafe. @@ -135,6 +144,25 @@ impl ModuleMetadata { )) } + /// # Safety + /// + /// This method is unsafe. + /// Please check `ModuleMetadata::deserialize` for more details. + fn archive_from_slice_checked( + metadata_slice: &[u8], + ) -> Result<&ArchivedModuleMetadata, DeserializeError> { + if metadata_slice.len() < 8 { + return Err(DeserializeError::Incompatible( + "invalid serialized ModuleMetadata".into(), + )); + } + let mut pos: [u8; 8] = Default::default(); + pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]); + let pos: u64 = u64::from_le_bytes(pos); + check_archived_value::(&metadata_slice[..metadata_slice.len() - 8], pos as usize) + .map_err(|e| DeserializeError::CorruptedBinary(e.to_string())) + } + /// Deserialize a compilation module from an archive pub fn deserialize_from_archive( archived: &ArchivedModuleMetadata, diff --git a/lib/types/src/compilation/trap.rs b/lib/types/src/compilation/trap.rs index 5f3e9cef81e..37467d49e4b 100644 --- a/lib/types/src/compilation/trap.rs +++ b/lib/types/src/compilation/trap.rs @@ -7,7 +7,9 @@ use serde::{Deserialize, Serialize}; /// Information about trap. #[cfg_attr(feature = "enable-serde", derive(Deserialize, Serialize))] -#[derive(RkyvSerialize, RkyvDeserialize, Archive, Clone, Debug, PartialEq, Eq)] +#[derive( + RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Clone, Debug, PartialEq, Eq, +)] #[archive(as = "Self")] pub struct TrapInformation { /// The offset of the trapping instruction in native code. It is relative to the beginning of the function. diff --git a/lib/types/src/compilation/unwind.rs b/lib/types/src/compilation/unwind.rs index 249b173c78b..384c5aed3a5 100644 --- a/lib/types/src/compilation/unwind.rs +++ b/lib/types/src/compilation/unwind.rs @@ -19,6 +19,7 @@ use serde::{Deserialize, Serialize}; /// [unwind info]: https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64?view=vs-2019 #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)] +#[archive_attr(derive(rkyv::CheckBytes))] pub enum CompiledFunctionUnwindInfo { /// Windows UNWIND_INFO. WindowsX64(Vec), diff --git a/lib/types/src/entity/primary_map.rs b/lib/types/src/entity/primary_map.rs index e1c1927fd3e..7f469b9f935 100644 --- a/lib/types/src/entity/primary_map.rs +++ b/lib/types/src/entity/primary_map.rs @@ -34,6 +34,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct PrimaryMap where K: EntityRef, diff --git a/lib/types/src/entity/secondary_map.rs b/lib/types/src/entity/secondary_map.rs index f9df40abf01..894dd810368 100644 --- a/lib/types/src/entity/secondary_map.rs +++ b/lib/types/src/entity/secondary_map.rs @@ -28,6 +28,7 @@ use serde::{ /// The map does not track if an entry for a key has been inserted or not. Instead it behaves as if /// all keys have a default entry from the beginning. #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(rkyv::CheckBytes))] pub struct SecondaryMap where K: EntityRef, diff --git a/lib/types/src/features.rs b/lib/types/src/features.rs index 395c55d837b..54197762db5 100644 --- a/lib/types/src/features.rs +++ b/lib/types/src/features.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; /// Features usually have a corresponding [WebAssembly proposal]. /// /// [WebAssembly proposal]: https://github.com/WebAssembly/proposals -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, rkyv::CheckBytes)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] #[archive(as = "Self")] diff --git a/lib/types/src/indexes.rs b/lib/types/src/indexes.rs index ec855c61bfc..3bf3883f3f1 100644 --- a/lib/types/src/indexes.rs +++ b/lib/types/src/indexes.rs @@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize}; RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -49,6 +50,7 @@ entity_impl!(LocalMemoryIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -68,6 +70,7 @@ entity_impl!(LocalGlobalIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -87,6 +90,7 @@ entity_impl!(FunctionIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -106,6 +110,7 @@ entity_impl!(TableIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -125,6 +130,7 @@ entity_impl!(GlobalIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -144,6 +150,7 @@ entity_impl!(MemoryIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -163,6 +170,7 @@ entity_impl!(SignatureIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -182,6 +190,7 @@ entity_impl!(DataIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -201,6 +210,7 @@ entity_impl!(ElemIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] @@ -220,9 +230,11 @@ entity_impl!(CustomSectionIndex); RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] +#[repr(u8)] pub enum ExportIndex { /// Function export. Function(FunctionIndex), @@ -236,10 +248,21 @@ pub enum ExportIndex { /// An entity to import. #[derive( - Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, RkyvSerialize, RkyvDeserialize, Archive, + Clone, + Debug, + Hash, + PartialEq, + Eq, + PartialOrd, + Ord, + RkyvSerialize, + RkyvDeserialize, + Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] +#[repr(u8)] pub enum ImportIndex { /// Function import. Function(FunctionIndex), diff --git a/lib/types/src/initializers.rs b/lib/types/src/initializers.rs index a34deaf13bd..addfbe6c282 100644 --- a/lib/types/src/initializers.rs +++ b/lib/types/src/initializers.rs @@ -1,13 +1,14 @@ use crate::indexes::{FunctionIndex, GlobalIndex, MemoryIndex, TableIndex}; use crate::lib::std::boxed::Box; -use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; +use rkyv::{Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; /// A WebAssembly table initializer. #[derive(Clone, Debug, Hash, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[archive_attr(derive(CheckBytes))] pub struct TableInitializer { /// The index of a table to initialize. pub table_index: TableIndex, @@ -23,6 +24,7 @@ pub struct TableInitializer { /// should be performed. #[derive(Clone, Debug, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[archive_attr(derive(CheckBytes))] pub struct DataInitializerLocation { /// The index of the memory to initialize. pub memory_index: MemoryIndex, @@ -49,6 +51,7 @@ pub struct DataInitializer<'data> { /// holding a reference to it #[derive(Debug, Clone, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[archive_attr(derive(CheckBytes))] pub struct OwnedDataInitializer { /// The location where the initialization is to be performed. pub location: DataInitializerLocation, diff --git a/lib/types/src/libcalls.rs b/lib/types/src/libcalls.rs index e58d52ff817..21222c588d6 100644 --- a/lib/types/src/libcalls.rs +++ b/lib/types/src/libcalls.rs @@ -18,9 +18,11 @@ use std::fmt; RkyvSerialize, RkyvDeserialize, Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] +#[repr(u16)] pub enum LibCall { /// ceil.f32 CeilF32, diff --git a/lib/types/src/memory.rs b/lib/types/src/memory.rs index 87132363523..3f2cab59cc2 100644 --- a/lib/types/src/memory.rs +++ b/lib/types/src/memory.rs @@ -7,9 +7,21 @@ use std::iter::Sum; use std::ops::{Add, AddAssign}; /// Implementation styles for WebAssembly linear memory. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, RkyvSerialize, RkyvDeserialize, Archive)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + Hash, + RkyvSerialize, + RkyvDeserialize, + Archive, + rkyv::CheckBytes, +)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] +#[repr(u8)] pub enum MemoryStyle { /// The actual memory can be resized and moved. Dynamic { diff --git a/lib/types/src/module.rs b/lib/types/src/module.rs index 08805900463..38afacc3daa 100644 --- a/lib/types/src/module.rs +++ b/lib/types/src/module.rs @@ -14,8 +14,8 @@ use crate::{ use indexmap::IndexMap; use rkyv::{ de::SharedDeserializeRegistry, ser::ScratchSpace, ser::Serializer, - ser::SharedSerializeRegistry, Archive, Archived, Deserialize as RkyvDeserialize, Fallible, - Serialize as RkyvSerialize, + ser::SharedSerializeRegistry, Archive, Archived, CheckBytes, Deserialize as RkyvDeserialize, + Fallible, Serialize as RkyvSerialize, }; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -26,6 +26,7 @@ use std::iter::ExactSizeIterator; use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; #[derive(Debug, Clone, RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(CheckBytes))] pub struct ModuleId { id: usize, } @@ -48,6 +49,7 @@ impl Default for ModuleId { /// Hash key of an import #[derive(Debug, Hash, Eq, PartialEq, Clone, Default, RkyvSerialize, RkyvDeserialize, Archive)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] +#[archive_attr(derive(CheckBytes, PartialEq, Eq, Hash))] pub struct ImportKey { /// Module name pub module: String, @@ -177,6 +179,7 @@ pub struct ModuleInfo { /// Mirror version of ModuleInfo that can derive rkyv traits #[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(CheckBytes))] pub struct ArchivableModuleInfo { name: Option, imports: IndexMap, diff --git a/lib/types/src/serialize.rs b/lib/types/src/serialize.rs index 462ebbd52ff..2c294dc2718 100644 --- a/lib/types/src/serialize.rs +++ b/lib/types/src/serialize.rs @@ -6,9 +6,10 @@ use crate::{ SerializeError, SignatureIndex, TableIndex, TableStyle, }; use enumset::EnumSet; +use rkyv::check_archived_value; use rkyv::{ archived_value, de::deserializers::SharedDeserializeMap, ser::serializers::AllocSerializer, - ser::Serializer as RkyvSerializer, Archive, Deserialize as RkyvDeserialize, + ser::Serializer as RkyvSerializer, Archive, CheckBytes, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize, }; use std::convert::TryInto; @@ -17,6 +18,7 @@ use std::mem; /// The compilation related data for a serialized modules #[derive(Archive, Default, RkyvDeserialize, RkyvSerialize)] #[allow(missing_docs)] +#[archive_attr(derive(CheckBytes))] pub struct SerializableCompilation { pub function_bodies: PrimaryMap, pub function_relocations: PrimaryMap>, @@ -51,6 +53,7 @@ impl SerializableCompilation { /// Serializable struct that is able to serialize from and to a `ArtifactInfo`. #[derive(Archive, RkyvDeserialize, RkyvSerialize)] #[allow(missing_docs)] +#[archive_attr(derive(CheckBytes))] pub struct SerializableModule { /// The main serializable compilation object pub compilation: SerializableCompilation, @@ -96,6 +99,16 @@ impl SerializableModule { Self::deserialize_from_archive(archived) } + /// Deserialize a Module from a slice. + /// The slice must have the following format: + /// RKYV serialization (any length) + POS (8 bytes) + /// + /// Unlike [`Self::deserialize`], this function will validate the data. + pub fn deserialize_checked(metadata_slice: &[u8]) -> Result { + let archived = Self::archive_from_slice_checked(metadata_slice)?; + Self::deserialize_from_archive(archived) + } + /// # Safety /// /// This method is unsafe. @@ -117,6 +130,25 @@ impl SerializableModule { )) } + /// Deserialize an archived module. + /// + /// In contrast to [`Self::deserialize`], this method performs validation + /// and is not unsafe. + fn archive_from_slice_checked( + metadata_slice: &[u8], + ) -> Result<&ArchivedSerializableModule, DeserializeError> { + if metadata_slice.len() < 8 { + return Err(DeserializeError::Incompatible( + "invalid serialized data".into(), + )); + } + let mut pos: [u8; 8] = Default::default(); + pos.copy_from_slice(&metadata_slice[metadata_slice.len() - 8..metadata_slice.len()]); + let pos: u64 = u64::from_le_bytes(pos); + check_archived_value::(&metadata_slice[..metadata_slice.len() - 8], pos as usize) + .map_err(|err| DeserializeError::CorruptedBinary(err.to_string())) + } + /// Deserialize a compilation module from an archive pub fn deserialize_from_archive( archived: &ArchivedSerializableModule, @@ -175,7 +207,7 @@ pub struct MetadataHeader { impl MetadataHeader { /// Current ABI version. Increment this any time breaking changes are made /// to the format of the serialized data. - const CURRENT_VERSION: u32 = 3; + const CURRENT_VERSION: u32 = 4; /// Magic number to identify wasmer metadata. const MAGIC: [u8; 8] = *b"WASMER\0\0"; diff --git a/lib/types/src/table.rs b/lib/types/src/table.rs index 50919f06d78..0b155d1f881 100644 --- a/lib/types/src/table.rs +++ b/lib/types/src/table.rs @@ -1,11 +1,15 @@ +use bytecheck::CheckBytes; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; /// Implementation styles for WebAssembly tables. -#[derive(Debug, Clone, Hash, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive)] +#[derive( + Debug, Clone, Hash, PartialEq, Eq, RkyvSerialize, RkyvDeserialize, Archive, CheckBytes, +)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] +#[repr(u8)] pub enum TableStyle { /// Signatures are stored in the table and checked in the caller. CallerChecksSignature, diff --git a/lib/types/src/trapcode.rs b/lib/types/src/trapcode.rs index 75c4e27d27b..88a4bf5ddc7 100644 --- a/lib/types/src/trapcode.rs +++ b/lib/types/src/trapcode.rs @@ -14,7 +14,17 @@ use thiserror::Error; /// /// All trap instructions have an explicit trap code. #[derive( - Clone, Copy, PartialEq, Eq, Debug, Hash, Error, RkyvSerialize, RkyvDeserialize, Archive, + Clone, + Copy, + PartialEq, + Eq, + Debug, + Hash, + Error, + RkyvSerialize, + RkyvDeserialize, + Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[repr(u32)] diff --git a/lib/types/src/types.rs b/lib/types/src/types.rs index ec26e089f07..588a4e13cc2 100644 --- a/lib/types/src/types.rs +++ b/lib/types/src/types.rs @@ -6,6 +6,7 @@ use crate::lib::std::string::{String, ToString}; use crate::lib::std::vec::Vec; use crate::units::Pages; +use bytecheck::CheckBytes; use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize}; #[cfg(feature = "enable-serde")] use serde::{Deserialize, Serialize}; @@ -17,8 +18,9 @@ use serde::{Deserialize, Serialize}; /// A list of all possible value types in WebAssembly. #[derive(Copy, Debug, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes)] #[archive(as = "Self")] +#[repr(u8)] pub enum Type { /// Signed 32 bit integer. I32, @@ -58,10 +60,10 @@ impl fmt::Display for Type { } } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +/// The WebAssembly V128 type +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, CheckBytes)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] -/// The WebAssembly V128 type #[archive(as = "Self")] pub struct V128(pub(crate) [u8; 16]); @@ -240,6 +242,7 @@ impl ExternType { #[derive(Debug, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(CheckBytes))] pub struct FunctionType { /// The parameters of the function params: Box<[Type]>, @@ -323,10 +326,11 @@ impl From<&Self> for FunctionType { } /// Indicator of whether a global is mutable or not -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, CheckBytes)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] #[archive(as = "Self")] +#[repr(u8)] pub enum Mutability { /// The global is constant and its value does not change Const, @@ -361,7 +365,7 @@ impl From for bool { } /// WebAssembly global. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, CheckBytes)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] #[archive(as = "Self")] @@ -408,8 +412,9 @@ impl fmt::Display for GlobalType { /// Globals are initialized via the `const` operators or by referring to another import. #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] -#[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[derive(RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes)] #[archive(as = "Self")] +#[repr(u8)] pub enum GlobalInit { /// An `i32.const`. I32Const(i32), @@ -442,6 +447,7 @@ pub enum GlobalInit { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(CheckBytes))] pub struct TableType { /// The type of data stored in elements of the table. pub ty: Type, @@ -482,6 +488,7 @@ impl fmt::Display for TableType { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[derive(RkyvSerialize, RkyvDeserialize, Archive)] +#[archive_attr(derive(CheckBytes))] pub struct MemoryType { /// The minimum number of pages in the memory. pub minimum: Pages, diff --git a/lib/types/src/units.rs b/lib/types/src/units.rs index c19250252b7..60e488b4a31 100644 --- a/lib/types/src/units.rs +++ b/lib/types/src/units.rs @@ -21,7 +21,17 @@ pub const WASM_MIN_PAGES: u32 = 0x100; /// Units of WebAssembly pages (as specified to be 65,536 bytes). #[derive( - Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RkyvSerialize, RkyvDeserialize, Archive, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + RkyvSerialize, + RkyvDeserialize, + Archive, + rkyv::CheckBytes, )] #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] #[archive(as = "Self")] diff --git a/lib/wasi/src/bin_factory/module_cache.rs b/lib/wasi/src/bin_factory/module_cache.rs index 0330bd0beef..6eff3d6eb73 100644 --- a/lib/wasi/src/bin_factory/module_cache.rs +++ b/lib/wasi/src/bin_factory/module_cache.rs @@ -230,7 +230,15 @@ impl ModuleCache { let module_bytes = bytes::Bytes::from(data); // Load the module - let module = unsafe { Module::deserialize(engine, &module_bytes[..]).unwrap() }; + let module = match Module::deserialize_checked(engine, &module_bytes[..]) { + Ok(m) => m, + Err(err) => { + tracing::error!( + "failed to deserialize module with hash '{data_hash}': {err}" + ); + return None; + } + }; if let Some(cache) = &self.cached_modules { let mut cache = cache.write().unwrap();