Validate that CPU features are supported when instantiating a module

This commit is contained in:
Amanieu d'Antras
2021-11-23 15:21:34 +00:00
parent 4f65a561ee
commit a603c33def
18 changed files with 89 additions and 17 deletions

5
Cargo.lock generated
View File

@@ -2900,6 +2900,7 @@ name = "wasmer-engine"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"enumset",
"lazy_static", "lazy_static",
"loupe", "loupe",
"memmap2", "memmap2",
@@ -2919,6 +2920,7 @@ name = "wasmer-engine-dummy"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"bincode", "bincode",
"enumset",
"loupe", "loupe",
"serde", "serde",
"serde_bytes", "serde_bytes",
@@ -2933,6 +2935,7 @@ name = "wasmer-engine-dylib"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"enumset",
"leb128", "leb128",
"libloading", "libloading",
"loupe", "loupe",
@@ -2954,6 +2957,7 @@ version = "2.0.0"
dependencies = [ dependencies = [
"bincode", "bincode",
"cfg-if 1.0.0", "cfg-if 1.0.0",
"enumset",
"leb128", "leb128",
"libloading", "libloading",
"loupe", "loupe",
@@ -2972,6 +2976,7 @@ name = "wasmer-engine-universal"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"enumset",
"leb128", "leb128",
"loupe", "loupe",
"region", "region",

View File

@@ -58,6 +58,11 @@ pub enum InstantiationError {
#[error(transparent)] #[error(transparent)]
Start(RuntimeError), 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 occurred when initializing the host environment.
#[error(transparent)] #[error(transparent)]
HostEnvInitialization(HostEnvInitError), HostEnvInitialization(HostEnvInitError),
@@ -68,6 +73,7 @@ impl From<wasmer_engine::InstantiationError> for InstantiationError {
match other { match other {
wasmer_engine::InstantiationError::Link(e) => Self::Link(e), wasmer_engine::InstantiationError::Link(e) => Self::Link(e),
wasmer_engine::InstantiationError::Start(e) => Self::Start(e), wasmer_engine::InstantiationError::Start(e) => Self::Start(e),
wasmer_engine::InstantiationError::CpuFeature(e) => Self::CpuFeature(e),
} }
} }
} }

View File

@@ -73,6 +73,12 @@ pub unsafe extern "C" fn wasm_instance_new(
return None; return None;
} }
Err(e @ InstantiationError::CpuFeature(_)) => {
crate::error::update_last_error(e.to_string());
return None;
}
Err(InstantiationError::HostEnvInitialization(error)) => { Err(InstantiationError::HostEnvInitialization(error)) => {
crate::error::update_last_error(error); crate::error::update_last_error(error);

View File

@@ -25,6 +25,7 @@ tempfile = "3.1"
which = "4.0" which = "4.0"
rkyv = "0.6.1" rkyv = "0.6.1"
loupe = "0.1" loupe = "0.1"
enumset = "1.0"
[features] [features]
# Enable the `compiler` feature if you want the engine to compile # Enable the `compiler` feature if you want the engine to compile

View File

@@ -3,6 +3,7 @@
use crate::engine::{DylibEngine, DylibEngineInner}; use crate::engine::{DylibEngine, DylibEngineInner};
use crate::serialize::{ArchivedModuleMetadata, ModuleMetadata}; use crate::serialize::{ArchivedModuleMetadata, ModuleMetadata};
use enumset::EnumSet;
use libloading::{Library, Symbol as LibrarySymbol}; use libloading::{Library, Symbol as LibrarySymbol};
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::error::Error; use std::error::Error;
@@ -17,8 +18,8 @@ use tracing::log::error;
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use tracing::trace; use tracing::trace;
use wasmer_compiler::{ use wasmer_compiler::{
Architecture, CompileError, CompiledFunctionFrameInfo, Features, FunctionAddressMap, Architecture, CompileError, CompiledFunctionFrameInfo, CpuFeature, Features,
OperatingSystem, Symbol, SymbolRegistry, Triple, FunctionAddressMap, OperatingSystem, Symbol, SymbolRegistry, Triple,
}; };
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_compiler::{ use wasmer_compiler::{
@@ -211,6 +212,7 @@ impl DylibArtifact {
prefix: engine_inner.get_prefix(&data), prefix: engine_inner.get_prefix(&data),
data_initializers, data_initializers,
function_body_lengths, function_body_lengths,
cpu_features: target.cpu_features().as_u64(),
}; };
let serialized_data = metadata.serialize()?; let serialized_data = metadata.serialize()?;
@@ -800,6 +802,10 @@ impl Artifact for DylibArtifact {
&self.metadata.compile_info.features &self.metadata.compile_info.features
} }
fn cpu_features(&self) -> enumset::EnumSet<CpuFeature> {
EnumSet::from_u64(self.metadata.cpu_features)
}
fn data_initializers(&self) -> &[OwnedDataInitializer] { fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.metadata.data_initializers &*self.metadata.data_initializers
} }

View File

@@ -35,6 +35,7 @@ pub struct ModuleMetadata {
pub data_initializers: Box<[OwnedDataInitializer]>, pub data_initializers: Box<[OwnedDataInitializer]>,
// The function body lengths (used to find function by address) // The function body lengths (used to find function by address)
pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>, pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>,
pub cpu_features: u64,
} }
pub struct ModuleMetadataSymbolRegistry<'a> { pub struct ModuleMetadataSymbolRegistry<'a> {

View File

@@ -24,6 +24,7 @@ leb128 = "0.2"
libloading = "0.7" libloading = "0.7"
tempfile = "3.1" tempfile = "3.1"
loupe = "0.1" loupe = "0.1"
enumset = "1.0"
[features] [features]
# Enable the `compiler` feature if you want the engine to compile # Enable the `compiler` feature if you want the engine to compile

View File

@@ -3,12 +3,15 @@
use crate::engine::{StaticlibEngine, StaticlibEngineInner}; use crate::engine::{StaticlibEngine, StaticlibEngineInner};
use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry}; use crate::serialize::{ModuleMetadata, ModuleMetadataSymbolRegistry};
use enumset::EnumSet;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::error::Error; use std::error::Error;
use std::mem; use std::mem;
use std::sync::Arc; use std::sync::Arc;
use wasmer_compiler::{CompileError, Features, OperatingSystem, SymbolRegistry, Triple}; use wasmer_compiler::{
CompileError, CpuFeature, Features, OperatingSystem, SymbolRegistry, Triple,
};
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_compiler::{ use wasmer_compiler::{
CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain, CompileModuleInfo, Compiler, FunctionBodyData, ModuleEnvironment, ModuleMiddlewareChain,
@@ -182,6 +185,7 @@ impl StaticlibArtifact {
prefix: engine_inner.get_prefix(&data), prefix: engine_inner.get_prefix(&data),
data_initializers, data_initializers,
function_body_lengths, function_body_lengths,
cpu_features: target.cpu_features().as_u64(),
}; };
/* /*
@@ -453,6 +457,10 @@ impl Artifact for StaticlibArtifact {
&self.metadata.compile_info.features &self.metadata.compile_info.features
} }
fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(self.metadata.cpu_features)
}
fn data_initializers(&self) -> &[OwnedDataInitializer] { fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.metadata.data_initializers &*self.metadata.data_initializers
} }

View File

@@ -12,6 +12,7 @@ pub struct ModuleMetadata {
pub data_initializers: Box<[OwnedDataInitializer]>, pub data_initializers: Box<[OwnedDataInitializer]>,
// The function body lengths (used to find function by address) // The function body lengths (used to find function by address)
pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>, pub function_body_lengths: PrimaryMap<LocalFunctionIndex, u64>,
pub cpu_features: u64,
} }
#[derive(MemoryUsage)] #[derive(MemoryUsage)]

View File

@@ -11,8 +11,13 @@ readme = "README.md"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
wasmer-types = { path = "../types", version = "2.0.0", features = ["enable-rkyv"] } wasmer-types = { path = "../types", version = "2.0.0", features = [
wasmer-compiler = { path = "../compiler", version = "2.0.0", features = ["translator", "enable-rkyv"] } "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-vm = { path = "../vm", version = "2.0.0", features = ["enable-rkyv"] }
wasmer-engine = { path = "../engine", version = "2.0.0" } wasmer-engine = { path = "../engine", version = "2.0.0" }
# flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" } # flexbuffers = { path = "../../../flatbuffers/rust/flexbuffers", version = "0.1.0" }
@@ -21,6 +26,7 @@ cfg-if = "1.0"
leb128 = "0.2" leb128 = "0.2"
rkyv = "0.6.1" rkyv = "0.6.1"
loupe = "0.1" loupe = "0.1"
enumset = "1.0"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["winnt", "impl-default"] } winapi = { version = "0.3", features = ["winnt", "impl-default"] }

View File

@@ -6,9 +6,10 @@ use crate::link::link_module;
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use crate::serialize::SerializableCompilation; use crate::serialize::SerializableCompilation;
use crate::serialize::SerializableModule; use crate::serialize::SerializableModule;
use enumset::EnumSet;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use wasmer_compiler::{CompileError, Features, Triple}; use wasmer_compiler::{CompileError, CpuFeature, Features, Triple};
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment, ModuleMiddlewareChain}; use wasmer_compiler::{CompileModuleInfo, ModuleEnvironment, ModuleMiddlewareChain};
use wasmer_engine::{ use wasmer_engine::{
@@ -128,6 +129,7 @@ impl UniversalArtifact {
compilation: serializable_compilation, compilation: serializable_compilation,
compile_info, compile_info,
data_initializers, data_initializers,
cpu_features: engine.target().cpu_features().as_u64(),
}; };
Self::from_parts(&mut inner_engine, serializable) Self::from_parts(&mut inner_engine, serializable)
} }
@@ -307,6 +309,10 @@ impl Artifact for UniversalArtifact {
&self.serializable.compile_info.features &self.serializable.compile_info.features
} }
fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(self.serializable.cpu_features)
}
fn data_initializers(&self) -> &[OwnedDataInitializer] { fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.serializable.data_initializers &*self.serializable.data_initializers
} }

View File

@@ -38,6 +38,7 @@ pub struct SerializableModule {
pub compilation: SerializableCompilation, pub compilation: SerializableCompilation,
pub compile_info: CompileModuleInfo, pub compile_info: CompileModuleInfo,
pub data_initializers: Box<[OwnedDataInitializer]>, pub data_initializers: Box<[OwnedDataInitializer]>,
pub cpu_features: u64,
} }
fn to_serialize_error(err: impl std::error::Error) -> SerializeError { fn to_serialize_error(err: impl std::error::Error) -> SerializeError {

View File

@@ -25,6 +25,7 @@ serde = { version = "1.0", features = ["derive", "rc"] }
serde_bytes = { version = "0.11" } serde_bytes = { version = "0.11" }
lazy_static = "1.4" lazy_static = "1.4"
loupe = "0.1" loupe = "0.1"
enumset = "1.0"
[badges] [badges]
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }

View File

@@ -1,12 +1,13 @@
use crate::{ use crate::{
resolve_imports, InstantiationError, Resolver, RuntimeError, SerializeError, Tunables, resolve_imports, InstantiationError, Resolver, RuntimeError, SerializeError, Tunables,
}; };
use enumset::EnumSet;
use loupe::MemoryUsage; use loupe::MemoryUsage;
use std::any::Any; use std::any::Any;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use wasmer_compiler::Features; use wasmer_compiler::{CpuFeature, Features};
use wasmer_types::entity::{BoxedSlice, PrimaryMap}; use wasmer_types::entity::{BoxedSlice, PrimaryMap};
use wasmer_types::{ use wasmer_types::{
DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo, DataInitializer, FunctionIndex, LocalFunctionIndex, MemoryIndex, ModuleInfo,
@@ -43,6 +44,9 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
/// Returns the features for this Artifact /// Returns the features for this Artifact
fn features(&self) -> &Features; 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`. /// Returns the memory styles associated with this `Artifact`.
fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle>; fn memory_styles(&self) -> &PrimaryMap<MemoryIndex, MemoryStyle>;
@@ -96,6 +100,16 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
resolver: &dyn Resolver, resolver: &dyn Resolver,
host_state: Box<dyn Any>, host_state: Box<dyn Any>,
) -> Result<InstanceHandle, InstantiationError> { ) -> 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()?; self.preinstantiate()?;
let module = self.module(); let module = self.module();

View File

@@ -91,6 +91,11 @@ pub enum InstantiationError {
#[error(transparent)] #[error(transparent)]
Link(LinkError), 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 /// A runtime error occured while invoking the start function
#[error(transparent)] #[error(transparent)]
Start(RuntimeError), Start(RuntimeError),

View File

@@ -257,7 +257,9 @@ fn trap_start_function_import(config: crate::Config) -> Result<()> {
.err() .err()
.unwrap(); .unwrap();
match err { match err {
InstantiationError::Link(_) | InstantiationError::HostEnvInitialization(_) => { InstantiationError::Link(_)
| InstantiationError::HostEnvInitialization(_)
| InstantiationError::CpuFeature(_) => {
panic!("It should be a start error") panic!("It should be a start error")
} }
InstantiationError::Start(err) => { InstantiationError::Start(err) => {

View File

@@ -16,19 +16,14 @@ serde = { version = "1.0", features = ["derive", "rc"], optional = true }
serde_bytes = { version = "0.11", optional = true } serde_bytes = { version = "0.11", optional = true }
bincode = { version = "1.2", optional = true } bincode = { version = "1.2", optional = true }
loupe = "0.1" loupe = "0.1"
enumset = "1.0"
[features] [features]
# Enable the `compiler` feature if you want the engine to compile # Enable the `compiler` feature if you want the engine to compile
# and not be only on headless mode. # and not be only on headless mode.
default = ["serialize", "compiler"] default = ["serialize", "compiler"]
compiler = [ compiler = ["wasmer-compiler/translator"]
"wasmer-compiler/translator" serialize = ["serde", "serde_bytes", "bincode"]
]
serialize = [
"serde",
"serde_bytes",
"bincode"
]
[badges] [badges]
# TODO: publish this crate again and deprecate it # TODO: publish this crate again and deprecate it

View File

@@ -2,13 +2,14 @@
//! done as separate steps. //! done as separate steps.
use crate::engine::DummyEngine; use crate::engine::DummyEngine;
use enumset::EnumSet;
use loupe::MemoryUsage; use loupe::MemoryUsage;
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
use wasmer_compiler::CompileError;
#[cfg(feature = "compiler")] #[cfg(feature = "compiler")]
use wasmer_compiler::ModuleEnvironment; use wasmer_compiler::ModuleEnvironment;
use wasmer_compiler::{CompileError, CpuFeature};
use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables}; use wasmer_engine::{Artifact, DeserializeError, Engine as _, SerializeError, Tunables};
use wasmer_types::entity::{BoxedSlice, PrimaryMap}; use wasmer_types::entity::{BoxedSlice, PrimaryMap};
use wasmer_types::{ use wasmer_types::{
@@ -30,6 +31,7 @@ pub struct DummyArtifactMetadata {
// Plans for that module // Plans for that module
pub memory_styles: PrimaryMap<MemoryIndex, MemoryStyle>, pub memory_styles: PrimaryMap<MemoryIndex, MemoryStyle>,
pub table_styles: PrimaryMap<TableIndex, TableStyle>, pub table_styles: PrimaryMap<TableIndex, TableStyle>,
pub cpu_features: u64,
} }
/// A Dummy artifact. /// A Dummy artifact.
@@ -104,6 +106,7 @@ impl DummyArtifact {
data_initializers, data_initializers,
memory_styles, memory_styles,
table_styles, table_styles,
cpu_features: engine.target().cpu_features().as_u64(),
}; };
Self::from_parts(&engine, metadata) Self::from_parts(&engine, metadata)
} }
@@ -211,6 +214,10 @@ impl Artifact for DummyArtifact {
&self.metadata.features &self.metadata.features
} }
fn cpu_features(&self) -> EnumSet<CpuFeature> {
EnumSet::from_u64(self.metadata.cpu_features)
}
fn data_initializers(&self) -> &[OwnedDataInitializer] { fn data_initializers(&self) -> &[OwnedDataInitializer] {
&*self.metadata.data_initializers &*self.metadata.data_initializers
} }