make Module::deserialize's version check optional via Config (#2945)

* make Module::deserialize's version check optional via Config

A SerializedModule contains the CARGO_PKG_VERSION string, which is
checked for equality when loading. This is a great guard-rail but
some users may want to disable this check (e.g. so they can implement
their own versioning scheme)

* rename config to deserialize_check_wasmtime_version

* add test

* fix doc links

* fix

* thank you rustdoc
This commit is contained in:
Pat Hickey
2021-06-04 12:18:02 -07:00
committed by GitHub
parent 357b4c7b60
commit 895ee2b85f
4 changed files with 36 additions and 8 deletions

View File

@@ -291,6 +291,7 @@ pub struct Config {
#[cfg(feature = "async")] #[cfg(feature = "async")]
pub(crate) async_stack_size: usize, pub(crate) async_stack_size: usize,
pub(crate) async_support: bool, pub(crate) async_support: bool,
pub(crate) deserialize_check_wasmtime_version: bool,
} }
impl Config { impl Config {
@@ -326,6 +327,7 @@ impl Config {
#[cfg(feature = "async")] #[cfg(feature = "async")]
async_stack_size: 2 << 20, async_stack_size: 2 << 20,
async_support: false, async_support: false,
deserialize_check_wasmtime_version: true,
}; };
ret.cranelift_debug_verifier(false); ret.cranelift_debug_verifier(false);
ret.cranelift_opt_level(OptLevel::Speed); ret.cranelift_opt_level(OptLevel::Speed);
@@ -1093,6 +1095,20 @@ impl Config {
self self
} }
/// Configure whether deserialized modules should validate version
/// information. This only effects [`crate::Module::deserialize()`], which is
/// used to load compiled code from trusted sources. When true,
/// [`crate::Module::deserialize()`] verifies that the wasmtime crate's
/// `CARGO_PKG_VERSION` matches with the version in the binary, which was
/// produced by [`crate::Module::serialize`] or
/// [`crate::Engine::precompile_module`].
///
/// This value defaults to true.
pub fn deserialize_check_wasmtime_version(&mut self, check: bool) -> &mut Self {
self.deserialize_check_wasmtime_version = check;
self
}
pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> { pub(crate) fn target_isa(&self) -> Box<dyn TargetIsa> {
self.isa_flags self.isa_flags
.clone() .clone()

View File

@@ -356,7 +356,10 @@ impl Module {
/// blobs across versions of wasmtime you can be safely guaranteed that /// blobs across versions of wasmtime you can be safely guaranteed that
/// future versions of wasmtime will reject old cache entries). /// future versions of wasmtime will reject old cache entries).
pub unsafe fn deserialize(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> { pub unsafe fn deserialize(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result<Module> {
let module = SerializedModule::from_bytes(bytes.as_ref())?; let module = SerializedModule::from_bytes(
bytes.as_ref(),
engine.config().deserialize_check_wasmtime_version,
)?;
module.into_module(engine) module.into_module(engine)
} }

View File

@@ -329,7 +329,7 @@ impl<'a> SerializedModule<'a> {
Ok(bytes) Ok(bytes)
} }
pub fn from_bytes(bytes: &[u8]) -> Result<Self> { pub fn from_bytes(bytes: &[u8], check_version: bool) -> Result<Self> {
if !bytes.starts_with(HEADER) { if !bytes.starts_with(HEADER) {
bail!("bytes are not a compatible serialized wasmtime module"); bail!("bytes are not a compatible serialized wasmtime module");
} }
@@ -345,12 +345,14 @@ impl<'a> SerializedModule<'a> {
bail!("serialized data is malformed"); bail!("serialized data is malformed");
} }
let version = std::str::from_utf8(&bytes[1..1 + version_len])?; if check_version {
if version != env!("CARGO_PKG_VERSION") { let version = std::str::from_utf8(&bytes[1..1 + version_len])?;
bail!( if version != env!("CARGO_PKG_VERSION") {
"Module was compiled with incompatible Wasmtime version '{}'", bail!(
version "Module was compiled with incompatible Wasmtime version '{}'",
); version
);
}
} }
Ok(bincode_options() Ok(bincode_options()

View File

@@ -24,6 +24,13 @@ fn test_version_mismatch() -> Result<()> {
.starts_with("Module was compiled with incompatible Wasmtime version")), .starts_with("Module was compiled with incompatible Wasmtime version")),
} }
// Test deserialize_check_wasmtime_version, which disables the logic which rejects the above.
let mut config = Config::new();
config.deserialize_check_wasmtime_version(false);
let engine = Engine::new(&config).unwrap();
unsafe { Module::deserialize(&engine, &buffer) }
.expect("module with corrupt version should deserialize when check is disabled");
Ok(()) Ok(())
} }