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

Disable post-MVP Wasm features #386

Merged
merged 5 commits into from
Apr 3, 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
10 changes: 10 additions & 0 deletions lib/src/executor/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@
//!
//! The first variant used to be the default model when compiling to WebAssembly, but the second
//! variant (importing memory objects) is preferred nowadays.
//!
//! # Wasm features
//!
//! The WebAssembly specification is a moving one. The specification as it was when it launched
//! in 2017 is commonly referred to as "the MVP" (minimum viable product). Since then, various
//! extensions have been added to the WebAssembly format.
//!
//! The code in this module, however, doesn't allow any of the feature that were added post-MVP.
//! Trying to use WebAssembly code that uses one of these features will result in an error.
//!

mod interpreter;
#[cfg(all(target_arch = "x86_64", feature = "std"))]
Expand Down
17 changes: 16 additions & 1 deletion lib/src/executor/vm/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,22 @@ impl InterpreterPrototype {
module_bytes: &[u8],
symbols: &mut dyn FnMut(&str, &str, &Signature) -> Result<usize, ()>,
) -> Result<Self, NewErr> {
let engine = wasmi::Engine::default(); // TODO: investigate config
let engine = {
let mut config = wasmi::Config::default();

// Disable all the post-MVP wasm features.
config.wasm_sign_extension(false);
config.wasm_reference_types(false);
config.wasm_bulk_memory(false);
config.wasm_multi_value(false);
config.wasm_extended_const(false);
config.wasm_mutable_global(false);
config.wasm_saturating_float_to_int(false);
config.wasm_tail_call(false);

wasmi::Engine::new(&config)
};

let module = wasmi::Module::new(&engine, module_bytes)
.map_err(|err| NewErr::InvalidWasm(err.to_string()))?;

Expand Down
12 changes: 12 additions & 0 deletions lib/src/executor/vm/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ impl JitPrototype {
// environment variables whatsoever. Whether to use `Enable` or `Disable` below isn't
// very important, so long as it is not `Environment`.
config.wasm_backtrace_details(wasmtime::WasmBacktraceDetails::Enable);

// Disable all post-MVP wasm features.
// Some of these configuration options are `true` by default while some others are `false`
// by default, but we just disable them all to be sure.
config.wasm_threads(false);
config.wasm_reference_types(false);
config.wasm_simd(false);
config.wasm_bulk_memory(false);
config.wasm_multi_value(false);
config.wasm_multi_memory(false);
config.wasm_memory64(false);

let engine =
wasmtime::Engine::new(&config).map_err(|err| NewErr::InvalidWasm(err.to_string()))?;

Expand Down
254 changes: 250 additions & 4 deletions lib/src/executor/vm/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ fn unsupported_signature() {
let module_bytes = wat::parse_str(
r#"
(module
(import "host" "hello" (func $host_hello (param i32) (param externref) (result i32)))
(import "host" "hello" (func $host_hello (param i32) (param f64) (result i32)))
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result i32) i32.const 0)
)
Expand Down Expand Up @@ -460,7 +460,7 @@ fn call_signature_not_supported() {
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result externref) unreachable)
(func (export "hello") (result f64) unreachable)
)
"#,
)
Expand Down Expand Up @@ -725,7 +725,8 @@ fn memory_grow_detects_limit_within_host_function() {
}
}

#[test]
// TODO: re-enable this test if the `mutable-globals` feature is enabled
/*#[test]
fn globals_reinitialized_after_reset() {
let module_bytes = wat::parse_str(
r#"
Expand Down Expand Up @@ -762,7 +763,7 @@ fn globals_reinitialized_after_reset() {
let mut prototype = vm.into_prototype();
assert_eq!(prototype.global_value("myglob").unwrap(), 5);
}
}
}*/

#[test]
fn memory_zeroed_after_reset() {
Expand Down Expand Up @@ -841,4 +842,249 @@ fn memory_zeroed_after_prepare() {
}
}

#[test]
fn feature_disabled_signext() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result i64)
(i64.const 2)
i64.extend32_s
)
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
// TODO: wasmtime doesn't allow disabling sign-ext /!\ test is faulty /!\ figure out what to do
// TODO: see https://github.com/paritytech/substrate/issues/10707#issuecomment-1494081313
if Some(exec_hint) == super::ExecHint::force_wasmtime_if_available() {
continue;
}

assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_saturated_float_to_int() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result i64)
(f32.const 2)
i64.trunc_sat_f32_s
)
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
// TODO: wasmtime doesn't allow disabling this feature /!\ test is faulty /!\ figure out what to do
// TODO: see https://github.com/paritytech/substrate/issues/10707#issuecomment-1494081313
if Some(exec_hint) == super::ExecHint::force_wasmtime_if_available() {
continue;
}

assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_threads() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result i64)
(atomic.fence)
i64.const 2
)
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_reference_type() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result externref) unreachable)
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_bulk_memory() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (result i64)
(memory.fill (i32.const 0) (i32.const 0) (i32.const 0))
i64.const 2
)
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_multi_value() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func (export "hello") (param i64 i64) (result i64 i64 i64)
(local.get 0) (local.get 1) (local.get 0)
)
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_memory64() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem i64 0 4096))
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_mutable_globals() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(global $myglob (export "myglob") (mut i32) (i32.const 5))
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
// TODO: wasmtime doesn't allow disabling this feature /!\ test is faulty /!\ figure out what to do
// TODO: see https://github.com/paritytech/substrate/issues/10707#issuecomment-1494081313
if Some(exec_hint) == super::ExecHint::force_wasmtime_if_available() {
continue;
}

assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

#[test]
fn feature_disabled_tail_call() {
let module_bytes = wat::parse_str(
r#"
(module
(import "env" "memory" (memory $mem 0 4096))
(func $fac (param $x i64) (result i64)
(return_call $fac-aux (get_local $x) (i64.const 1))
)
(func $fac-aux (param $x i64) (param $r i64) (result i64)
(if (i64.eqz (get_local $x))
(then (return (get_local $r)))
(else
(return_call $fac-aux
(i64.sub (get_local $x) (i64.const 1))
(i64.mul (get_local $x) (get_local $r))
)
)
)
)
)
"#,
)
.unwrap();

for exec_hint in super::ExecHint::available_engines() {
assert!(super::VirtualMachinePrototype::new(super::Config {
module_bytes: &module_bytes,
exec_hint,
symbols: &mut |_, _, _| Ok(0),
})
.is_err());
}
}

// TODO: check that the SIMD feature is disabled: https://github.com/WebAssembly/simd/blob/master/proposals/simd/SIMD.md
// TODO: check that the extended-const feature is disabled: https://github.com/WebAssembly/extended-const/blob/master/proposals/extended-const/Overview.md

// TODO: test for memory reads and writes, including within host functions
4 changes: 4 additions & 0 deletions wasm-node/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

- Removed support for the `ls` message in the multistream-select protocol, in accordance with the rest of the libp2p ecosystem. This message was in practice never used, and removing support for it simplifies the implementation. ([#379](https://github.com/smol-dot/smoldot/pull/379))

### Fixed

- Post-MVP WebAssembly features are now properly disabled when compiling runtimes. This rejects runtimes that Substrate would consider as invalid as well. ([#386](https://github.com/smol-dot/smoldot/pull/386))

## 1.0.1 - 2023-03-29

### Changed
Expand Down