Skip to content

Commit

Permalink
Validate that CPU features are supported when instantiating a module
Browse files Browse the repository at this point in the history
  • Loading branch information
Amanieu committed Nov 23, 2021
1 parent 4f65a56 commit a603c33
Show file tree
Hide file tree
Showing 18 changed files with 89 additions and 17 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

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

6 changes: 6 additions & 0 deletions lib/api/src/sys/instance.rs
Expand Up @@ -58,6 +58,11 @@ pub enum InstantiationError {
#[error(transparent)]
Start(RuntimeError),

/// The module was compiled with a CPU feature that is not available on
/// the current host.
#[error("missing requires CPU features: {0:?}")]
CpuFeature(String),

/// Error occurred when initializing the host environment.
#[error(transparent)]
HostEnvInitialization(HostEnvInitError),
Expand All @@ -68,6 +73,7 @@ impl From<wasmer_engine::InstantiationError> for InstantiationError {
match other {
wasmer_engine::InstantiationError::Link(e) => Self::Link(e),
wasmer_engine::InstantiationError::Start(e) => Self::Start(e),
wasmer_engine::InstantiationError::CpuFeature(e) => Self::CpuFeature(e),
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions lib/c-api/src/wasm_c_api/instance.rs
Expand Up @@ -73,6 +73,12 @@ pub unsafe extern "C" fn wasm_instance_new(
return None;
}

Err(e @ InstantiationError::CpuFeature(_)) => {
crate::error::update_last_error(e.to_string());

return None;
}

Err(InstantiationError::HostEnvInitialization(error)) => {
crate::error::update_last_error(error);

Expand Down
1 change: 1 addition & 0 deletions lib/engine-dylib/Cargo.toml
Expand Up @@ -25,6 +25,7 @@ tempfile = "3.1"
which = "4.0"
rkyv = "0.6.1"
loupe = "0.1"
enumset = "1.0"

[features]
# Enable the `compiler` feature if you want the engine to compile
Expand Down
10 changes: 8 additions & 2 deletions lib/engine-dylib/src/artifact.rs
Expand Up @@ -3,6 +3,7 @@

use crate::engine::{DylibEngine, DylibEngineInner};
use crate::serialize::{ArchivedModuleMetadata, ModuleMetadata};
use enumset::EnumSet;
use libloading::{Library, Symbol as LibrarySymbol};
use loupe::MemoryUsage;
use std::error::Error;
Expand All @@ -17,8 +18,8 @@ use tracing::log::error;
#[cfg(feature = "compiler")]
use tracing::trace;
use wasmer_compiler::{
Architecture, CompileError, CompiledFunctionFrameInfo, Features, FunctionAddressMap,
OperatingSystem, Symbol, SymbolRegistry, Triple,
Architecture, CompileError, CompiledFunctionFrameInfo, CpuFeature, Features,
FunctionAddressMap, OperatingSystem, Symbol, SymbolRegistry, Triple,
};
#[cfg(feature = "compiler")]
use wasmer_compiler::{
Expand Down Expand Up @@ -211,6 +212,7 @@ impl DylibArtifact {
prefix: engine_inner.get_prefix(&data),
data_initializers,
function_body_lengths,
cpu_features: target.cpu_features().as_u64(),
};

let serialized_data = metadata.serialize()?;
Expand Down Expand Up @@ -800,6 +802,10 @@ impl Artifact for DylibArtifact {
&self.metadata.compile_info.features
}

fn cpu_features(&self) -> enumset::EnumSet<CpuFeature> {
EnumSet::from_u64(self.metadata.cpu_features)
}

fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.metadata.data_initializers
}
Expand Down
1 change: 1 addition & 0 deletions lib/engine-dylib/src/serialize.rs
Expand Up @@ -35,6 +35,7 @@ pub struct ModuleMetadata {
pub data_initializers: Box<[OwnedDataInitializer]>,
// The function body lengths (used to find function by address)
pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>,
pub cpu_features: u64,
}

pub struct ModuleMetadataSymbolRegistry<'a> {
Expand Down
1 change: 1 addition & 0 deletions lib/engine-staticlib/Cargo.toml
Expand Up @@ -24,6 +24,7 @@ leb128 = "0.2"
libloading = "0.7"
tempfile = "3.1"
loupe = "0.1"
enumset = "1.0"

[features]
# Enable the `compiler` feature if you want the engine to compile
Expand Down
10 changes: 9 additions & 1 deletion lib/engine-staticlib/src/artifact.rs
Expand Up @@ -3,12 +3,15 @@

use crate::engine::{StaticlibEngine, StaticlibEngineInner};
use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry};
use enumset::EnumSet;
use loupe::MemoryUsage;
use std::collections::BTreeMap;
use std::error::Error;
use std::mem;
use std::sync::Arc;
use wasmer_compiler::{CompileError, Features, OperatingSystem, SymbolRegistry, Triple};
use wasmer_compiler::{
CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple,
};
#[cfg(feature = "compiler")]
use wasmer_compiler::{
CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain,
Expand Down Expand Up @@ -182,6 +185,7 @@ impl StaticlibArtifact {
prefix: engine_inner.get_prefix(&data),
data_initializers,
function_body_lengths,
cpu_features: target.cpu_features().as_u64(),
};

/*
Expand Down Expand Up @@ -453,6 +457,10 @@ impl Artifact for StaticlibArtifact {
&self.metadata.compile_info.features
}

fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(self.metadata.cpu_features)
}

fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.metadata.data_initializers
}
Expand Down
1 change: 1 addition & 0 deletions lib/engine-staticlib/src/serialize.rs
Expand Up @@ -12,6 +12,7 @@ pub struct ModuleMetadata {
pub data_initializers: Box<[OwnedDataInitializer]>,
// The function body lengths (used to find function by address)
pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>,
pub cpu_features: u64,
}

#[derive(MemoryUsage)]
Expand Down
10 changes: 8 additions & 2 deletions lib/engine-universal/Cargo.toml
Expand Up @@ -11,8 +11,13 @@ readme = "README.md"
edition = "2018"

[dependencies]
wasmer-types = { path = "../types", version = "2.0.0", features = ["enable-rkyv"] }
wasmer-compiler = { path = "../compiler", version = "2.0.0", features = ["translator", "enable-rkyv"] }
wasmer-types = { path = "../types", version = "2.0.0", features = [
"enable-rkyv",
] }
wasmer-compiler = { path = "../compiler", version = "2.0.0", features = [
"translator",
"enable-rkyv",
] }
wasmer-vm = { path = "../vm", version = "2.0.0", features = ["enable-rkyv"] }
wasmer-engine = { path = "../engine", version = "2.0.0" }
# flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" }
Expand All @@ -21,6 +26,7 @@ cfg-if = "1.0"
leb128 = "0.2"
rkyv = "0.6.1"
loupe = "0.1"
enumset = "1.0"

[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winnt", "impl-default"] }
Expand Down
8 changes: 7 additions & 1 deletion lib/engine-universal/src/artifact.rs
Expand Up @@ -6,9 +6,10 @@ use crate::link::link_module;
#[cfg(feature = "compiler")]
use crate::serialize::SerializableCompilation;
use crate::serialize::SerializableModule;
use enumset::EnumSet;
use loupe::MemoryUsage;
use std::sync::{Arc, Mutex};
use wasmer_compiler::{CompileError, Features, Triple};
use wasmer_compiler::{CompileError, CpuFeature, Features, Triple};
#[cfg(feature = "compiler")]
use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment, ModuleMiddlewareChain};
use wasmer_engine::{
Expand Down Expand Up @@ -128,6 +129,7 @@ impl UniversalArtifact {
compilation: serializable_compilation,
compile_info,
data_initializers,
cpu_features: engine.target().cpu_features().as_u64(),
};
Self::from_parts(&mut inner_engine, serializable)
}
Expand Down Expand Up @@ -307,6 +309,10 @@ impl Artifact for UniversalArtifact {
&self.serializable.compile_info.features
}

fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(self.serializable.cpu_features)
}

fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.serializable.data_initializers
}
Expand Down
1 change: 1 addition & 0 deletions lib/engine-universal/src/serialize.rs
Expand Up @@ -38,6 +38,7 @@ pub struct SerializableModule {
pub compilation: SerializableCompilation,
pub compile_info: CompileModuleInfo,
pub data_initializers: Box<[OwnedDataInitializer]>,
pub cpu_features: u64,

This comment has been minimized.

Copy link
@webmaster128

webmaster128 Feb 4, 2022

Contributor

Is this change serialization breaking (i.e. a module serialized with 2.0.0 cannot be deserialized with 2.1.0+)?

}

fn to_serialize_error(err: impl std::error::Error) -> SerializeError {
Expand Down
1 change: 1 addition & 0 deletions lib/engine/Cargo.toml
Expand Up @@ -25,6 +25,7 @@ serde = { version = "1.0", features = ["derive", "rc"] }
serde_bytes = { version = "0.11" }
lazy_static = "1.4"
loupe = "0.1"
enumset = "1.0"

[badges]
maintenance = { status = "actively-developed" }
16 changes: 15 additions & 1 deletion lib/engine/src/artifact.rs
@@ -1,12 +1,13 @@
use crate::{
resolve_imports, InstantiationError, Resolver, RuntimeError, SerializeError, Tunables,
};
use enumset::EnumSet;
use loupe::MemoryUsage;
use std::any::Any;
use std::fs;
use std::path::Path;
use std::sync::Arc;
use wasmer_compiler::Features;
use wasmer_compiler::{CpuFeature, Features};
use wasmer_types::entity::{BoxedSlice, PrimaryMap};
use wasmer_types::{
DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo,
Expand Down Expand Up @@ -43,6 +44,9 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
/// Returns the features for this Artifact
fn features(&self) -> &Features;

/// Returns the CPU features for this Artifact
fn cpu_features(&self) -> EnumSet<CpuFeature>;

/// Returns the memory styles associated with this `Artifact`.
fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle>;

Expand Down Expand Up @@ -96,6 +100,16 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
resolver: &dyn Resolver,
host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> {
// Validate the CPU features this module was compiled with against the
// host CPU features.
let host_cpu_features = CpuFeature::for_host();
if !host_cpu_features.is_superset(self.cpu_features()) {
Err(InstantiationError::CpuFeature(format!(
"{:?}",
self.cpu_features().difference(host_cpu_features)
)))?;
}

self.preinstantiate()?;

let module = self.module();
Expand Down
5 changes: 5 additions & 0 deletions lib/engine/src/error.rs
Expand Up @@ -91,6 +91,11 @@ pub enum InstantiationError {
#[error(transparent)]
Link(LinkError),

/// The module was compiled with a CPU feature that is not available on
/// the current host.
#[error("module compiled with CPU feature that is missing from host")]
CpuFeature(String),

/// A runtime error occured while invoking the start function
#[error(transparent)]
Start(RuntimeError),
Expand Down
4 changes: 3 additions & 1 deletion tests/compilers/traps.rs
Expand Up @@ -257,7 +257,9 @@ fn trap_start_function_import(config: crate::Config) -> Result<()> {
.err()
.unwrap();
match err {
InstantiationError::Link(_) | InstantiationError::HostEnvInitialization(_) => {
InstantiationError::Link(_)
| InstantiationError::HostEnvInitialization(_)
| InstantiationError::CpuFeature(_) => {
panic!("It should be a start error")
}
InstantiationError::Start(err) => {
Expand Down
11 changes: 3 additions & 8 deletions tests/lib/engine-dummy/Cargo.toml
Expand Up @@ -16,19 +16,14 @@ serde = { version = "1.0", features = ["derive", "rc"], optional = true }
serde_bytes = { version = "0.11", optional = true }
bincode = { version = "1.2", optional = true }
loupe = "0.1"
enumset = "1.0"

[features]
# Enable the `compiler` feature if you want the engine to compile
# and not be only on headless mode.
default = ["serialize", "compiler"]
compiler = [
"wasmer-compiler/translator"
]
serialize = [
"serde",
"serde_bytes",
"bincode"
]
compiler = ["wasmer-compiler/translator"]
serialize = ["serde", "serde_bytes", "bincode"]

[badges]
# TODO: publish this crate again and deprecate it
Expand Down
9 changes: 8 additions & 1 deletion tests/lib/engine-dummy/src/artifact.rs
Expand Up @@ -2,13 +2,14 @@
//! done as separate steps.

use crate::engine::DummyEngine;
use enumset::EnumSet;
use loupe::MemoryUsage;
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment;
use wasmer_compiler::{CompileError, CpuFeature};
use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables};
use wasmer_types::entity::{BoxedSlice, PrimaryMap};
use wasmer_types::{
Expand All @@ -30,6 +31,7 @@ pub struct DummyArtifactMetadata {
// Plans for that module
pub memory_styles: PrimaryMap<MemoryIndex, MemoryStyle>,
pub table_styles: PrimaryMap<TableIndex, TableStyle>,
pub cpu_features: u64,
}

/// A Dummy artifact.
Expand Down Expand Up @@ -104,6 +106,7 @@ impl DummyArtifact {
data_initializers,
memory_styles,
table_styles,
cpu_features: engine.target().cpu_features().as_u64(),
};
Self::from_parts(&engine, metadata)
}
Expand Down Expand Up @@ -211,6 +214,10 @@ impl Artifact for DummyArtifact {
&self.metadata.features
}

fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(self.metadata.cpu_features)
}

fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.metadata.data_initializers
}
Expand Down

0 comments on commit a603c33

Please sign in to comment.