Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate Module Artifacts (CLI + WASIX ModuleCache) #3693

Merged
merged 2 commits into from
Mar 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions lib/api/src/js/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, DeserializeError> {
#[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<Path>,
Expand All @@ -246,6 +258,14 @@ impl Module {
Self::deserialize(engine, bytes)
}

pub fn deserialize_from_file_checked(
engine: &impl AsEngineRef,
path: impl AsRef<Path>,
) -> Result<Self, DeserializeError> {
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
Expand Down
52 changes: 52 additions & 0 deletions lib/api/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Self, DeserializeError> {
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<Path>,
) -> Result<Self, DeserializeError> {
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.
///
Expand Down
25 changes: 25 additions & 0 deletions lib/api/src/sys/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,19 @@ impl Module {
Ok(Self::from_artifact(artifact))
}

pub fn deserialize_checked(
engine: &impl AsEngineRef,
bytes: impl IntoBytes,
) -> Result<Self, DeserializeError> {
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<Path>,
Expand All @@ -90,6 +103,18 @@ impl Module {
Ok(Self::from_artifact(artifact))
}

pub fn deserialize_from_file_checked(
engine: &impl AsEngineRef,
path: impl AsRef<Path>,
) -> Result<Self, DeserializeError> {
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<Artifact>) -> Self {
Self { artifact }
}
Expand Down
2 changes: 1 addition & 1 deletion lib/cli/src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
Expand Down
24 changes: 24 additions & 0 deletions lib/compiler/src/engine/artifact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, DeserializeError> {
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<Self, DeserializeError> {
if !ArtifactBuild::is_deserializable(bytes) {
let static_artifact = Self::deserialize_object(engine, bytes);
Expand Down
18 changes: 17 additions & 1 deletion lib/compiler/src/engine/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Arc<Artifact>, 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<Arc<Artifact>, 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,
Expand Down
3 changes: 2 additions & 1 deletion lib/types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions lib/types/src/compilation/address_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion lib/types/src/compilation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
///
Expand All @@ -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"))]
Expand All @@ -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,
Expand All @@ -74,7 +77,9 @@ pub type CustomSections = PrimaryMap<SectionIndex, CustomSection>;
/// 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.
Expand Down
1 change: 1 addition & 0 deletions lib/types/src/compilation/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 9 additions & 2 deletions lib/types/src/compilation/relocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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),
Expand Down
8 changes: 7 additions & 1 deletion lib/types/src/compilation/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize};
RkyvSerialize,
RkyvDeserialize,
Archive,
rkyv::CheckBytes,
Copy,
Clone,
PartialEq,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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<u8>);

impl SectionBody {
Expand Down
2 changes: 1 addition & 1 deletion lib/types/src/compilation/sourceloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading