Skip to content

Commit

Permalink
Disable post-MVP Wasm features (#386)
Browse files Browse the repository at this point in the history
* Disable post-MVP Wasm features

* PR number

* Adjust tests and add one for `mutable-globals`

* Check tail-call feature

* Document the features
  • Loading branch information
tomaka authored Apr 3, 2023
1 parent c0c5d21 commit 7581f75
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 5 deletions.
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

0 comments on commit 7581f75

Please sign in to comment.