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

Added support for Traps #5

Merged
merged 7 commits into from
Nov 6, 2018
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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- v4-cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }}
- run: sudo apt-get install -y cmake
- run: rustup default nightly-2018-10-07
- run: cargo test
- run: make test

- save_cache:
paths:
Expand Down
20 changes: 20 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ spin = "0.4.10"
log = "0.4.5"
target-lexicon = { version = "0.0.3", default-features = false }
libc = "0.2"
nix = "0.11"

[build-dependencies]
wabt = "0.6.0"
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ install:
cargo install --path .

test:
cargo test
cargo test -- --test-threads=1
4 changes: 3 additions & 1 deletion spectests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Currently supported command assertions:
- [x] AssertReturn _mostly implemented_ (it should support calls to named modules `(invoke $Xx "call")`).
- [x] AssertReturnCanonicalNan _fully implemented_
- [x] AssertReturnArithmeticNan _fully implemented_
- [ ] AssertTrap _not implemented yet_
- [x] AssertTrap _fully implemented_
- [x] AssertInvalid _Fully implemented_ (it should not require to do validation separate from compilation)
- [x] AssertMalformed _Fully implemented_
- [ ] AssertUninstantiable _not implemented yet_
Expand Down Expand Up @@ -119,3 +119,5 @@ There are some cases that we decided to skip for now to fasten the time to relea
- `nop.wast`
- `SKIP_MUTABLE_GLOBALS`: Right now the WASM parser can't validate a module with imported/exported mut globals. We decided to skip the tests until Cranelift and wasmparser can handle this (original spec proposal: https://github.com/WebAssembly/mutable-global). Spectests affected:
- `globals.wast`
- `SKIP_CALL_INDIRECT_TYPE_MISMATCH`: we implemented traps in a fast way. We haven't covered yet the type mismatch on `call_indirect`. Specs affected:
- `call_indirect.wast`
30 changes: 20 additions & 10 deletions spectests/call_indirect.wast
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,10 @@
(assert_return (invoke "dispatch" (i32.const 12) (i64.const 5)) (i64.const 120))
(assert_return (invoke "dispatch" (i32.const 13) (i64.const 5)) (i64.const 8))
(assert_return (invoke "dispatch" (i32.const 20) (i64.const 2)) (i64.const 2))
(assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch")
(assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch" (i32.const 0) (i64.const 2)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch" (i32.const 15) (i64.const 2)) "indirect call type mismatch")
(assert_trap (invoke "dispatch" (i32.const 29) (i64.const 2)) "undefined element")
(assert_trap (invoke "dispatch" (i32.const -1) (i64.const 2)) "undefined element")
(assert_trap (invoke "dispatch" (i32.const 1213432423) (i64.const 2)) "undefined element")
Expand All @@ -417,29 +419,37 @@
(assert_return (invoke "dispatch-structural-i64" (i32.const 12)) (i64.const 362880))
(assert_return (invoke "dispatch-structural-i64" (i32.const 13)) (i64.const 55))
(assert_return (invoke "dispatch-structural-i64" (i32.const 20)) (i64.const 9))
(assert_trap (invoke "dispatch-structural-i64" (i32.const 11)) "indirect call type mismatch")
(assert_trap (invoke "dispatch-structural-i64" (i32.const 22)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-i64" (i32.const 11)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-i64" (i32.const 22)) "indirect call type mismatch")

(assert_return (invoke "dispatch-structural-i32" (i32.const 4)) (i32.const 9))
(assert_return (invoke "dispatch-structural-i32" (i32.const 23)) (i32.const 362880))
(assert_return (invoke "dispatch-structural-i32" (i32.const 26)) (i32.const 55))
(assert_return (invoke "dispatch-structural-i32" (i32.const 19)) (i32.const 9))
(assert_trap (invoke "dispatch-structural-i32" (i32.const 9)) "indirect call type mismatch")
(assert_trap (invoke "dispatch-structural-i32" (i32.const 21)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-i32" (i32.const 9)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-i32" (i32.const 21)) "indirect call type mismatch")

(assert_return (invoke "dispatch-structural-f32" (i32.const 6)) (f32.const 9.0))
(assert_return (invoke "dispatch-structural-f32" (i32.const 24)) (f32.const 362880.0))
(assert_return (invoke "dispatch-structural-f32" (i32.const 27)) (f32.const 55.0))
(assert_return (invoke "dispatch-structural-f32" (i32.const 21)) (f32.const 9.0))
(assert_trap (invoke "dispatch-structural-f32" (i32.const 8)) "indirect call type mismatch")
(assert_trap (invoke "dispatch-structural-f32" (i32.const 19)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-f32" (i32.const 8)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-f32" (i32.const 19)) "indirect call type mismatch")

(assert_return (invoke "dispatch-structural-f64" (i32.const 7)) (f64.const 9.0))
(assert_return (invoke "dispatch-structural-f64" (i32.const 25)) (f64.const 362880.0))
(assert_return (invoke "dispatch-structural-f64" (i32.const 28)) (f64.const 55.0))
(assert_return (invoke "dispatch-structural-f64" (i32.const 22)) (f64.const 9.0))
(assert_trap (invoke "dispatch-structural-f64" (i32.const 10)) "indirect call type mismatch")
(assert_trap (invoke "dispatch-structural-f64" (i32.const 18)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-f64" (i32.const 10)) "indirect call type mismatch")
;; SKIP_CALL_INDIRECT_TYPE_MISMATCH
;; (assert_trap (invoke "dispatch-structural-f64" (i32.const 18)) "indirect call type mismatch")

(assert_return (invoke "fac-i64" (i64.const 0)) (i64.const 1))
(assert_return (invoke "fac-i64" (i64.const 1)) (i64.const 1))
Expand Down
53 changes: 48 additions & 5 deletions src/build_spectests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,14 @@ impl WastTestGenerator {
warnings,
dead_code
)]
use std::panic;
use wabt::wat2wasm;

use crate::webassembly::{{instantiate, compile, ImportObject, ResultObject, VmCtx, Export}};
use super::_common::{{
spectest_importobject,
NaNCheck,
}};
use wabt::wat2wasm;\n\n",
}};\n\n",
self.filename
));
while let Some(Command { line, kind }) = &self.script_parser.next().unwrap() {
Expand Down Expand Up @@ -277,7 +279,11 @@ fn {}_assert_invalid() {{

fn visit_assert_return_arithmetic_nan(&mut self, action: &Action) {
match action {
Action::Invoke { module, field, args, } => {
Action::Invoke {
module,
field,
args,
} => {
let mut return_type = wabt2rust_type(&args[0]);
let mut func_return = format!(" -> {}", return_type);
let assertion = String::from("assert!(result.is_quiet_nan())");
Expand Down Expand Up @@ -326,7 +332,11 @@ fn {}_assert_invalid() {{
// and wabt does gives us the `expected` result
fn visit_assert_return_canonical_nan(&mut self, action: &Action) {
match action {
Action::Invoke { module, field, args, } => {
Action::Invoke {
module,
field,
args,
} => {
let mut return_type = match &field.as_str() {
&"f64.promote_f32" => String::from("f64"),
&"f32.promote_f64" => String::from("f32"),
Expand Down Expand Up @@ -487,6 +497,39 @@ fn {}_assert_malformed() {{
.or_insert(Vec::new())
.push(action_fn_name.unwrap());
}
fn visit_assert_trap(&mut self, action: &Action) {
let action_fn_name = self.visit_action(action, None);

if action_fn_name.is_none() {
return;
}
let trap_func_name = format!("{}_assert_trap", self.command_name());
self.buffer.push_str(
format!(
"
#[test]
fn {}() {{
let result_object = create_module_{}();
let vm_context = result_object.instance.generate_context();
let result = panic::catch_unwind(|| {{
{}(&result_object, &vm_context);
}});
assert!(result.is_err());
}}\n",
trap_func_name,
self.last_module,
action_fn_name.unwrap(),
)
.as_str(),
);

// We don't group trap calls as they may cause memory faults
// on the instance memory. So we test them alone.
// self.module_calls
// .entry(self.last_module)
// .or_insert(Vec::new())
// .push(trap_func_name);
}

fn visit_command(&mut self, cmd: &CommandKind) {
match cmd {
Expand All @@ -503,7 +546,7 @@ fn {}_assert_malformed() {{
self.visit_assert_return_arithmetic_nan(action);
}
CommandKind::AssertTrap { action, message: _ } => {
// Do nothing for now
self.visit_assert_trap(action);
}
CommandKind::AssertInvalid { module, message: _ } => {
self.visit_assert_invalid(module);
Expand Down
5 changes: 5 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
#[macro_export]
macro_rules! get_instance_function {
($instance:expr, $func_index:expr) => {{
use crate::sighandler::install_sighandler;
use std::mem;

unsafe {
install_sighandler();
};
let func_addr = $instance.get_function_pointer($func_index);
unsafe { mem::transmute(func_addr) }
}};
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern crate cranelift_wasm;
extern crate wabt;
#[macro_use]
extern crate target_lexicon;
extern crate nix;
extern crate spin;

// use std::alloc::System;
Expand All @@ -37,6 +38,7 @@ use wabt::wat2wasm;
mod macros;
pub mod common;
pub mod integrations;
pub mod sighandler;
#[cfg(test)]
mod spectests;
pub mod webassembly;
Expand Down
39 changes: 39 additions & 0 deletions src/sighandler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//! We install signal handlers to handle WebAssembly traps within
//! our Rust code. Otherwise we will have errors that stop the Rust process
//! such as `process didn't exit successfully: ... (signal: 8, SIGFPE: erroneous arithmetic operation)`
//!
//! Please read more about this here: https://github.com/CraneStation/wasmtime/issues/15
//! This code is inspired by: https://github.com/pepyakin/wasmtime/commit/625a2b6c0815b21996e111da51b9664feb174622
use nix::sys::signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
};

pub unsafe fn install_sighandler() {
let sa = SigAction::new(
SigHandler::Handler(signal_trap_handler),
SaFlags::empty(),
SigSet::empty(),
);
sigaction(SIGFPE, &sa).unwrap();
sigaction(SIGILL, &sa).unwrap();
sigaction(SIGSEGV, &sa).unwrap();
sigaction(SIGBUS, &sa).unwrap();
let result = setjmp((&mut setjmp_buffer[..]).as_mut_ptr() as *mut ::nix::libc::c_void);
if result != 0 {
panic!("Signal Error: {}", result);
}
}

static mut setjmp_buffer: [::nix::libc::c_int; 27] = [0; 27];
extern "C" {
fn setjmp(env: *mut ::nix::libc::c_void) -> ::nix::libc::c_int;
fn longjmp(env: *mut ::nix::libc::c_void, val: ::nix::libc::c_int);
}
extern "C" fn signal_trap_handler(_: ::nix::libc::c_int) {
unsafe {
longjmp(
(&mut setjmp_buffer).as_mut_ptr() as *mut ::nix::libc::c_void,
3,
);
}
}
Loading