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

Exit with a more severe error code if the program traps. #1274

Merged
merged 8 commits into from
Mar 11, 2020
30 changes: 27 additions & 3 deletions src/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use std::{
ffi::{OsStr, OsString},
fs::File,
path::{Component, Path, PathBuf},
process,
};
use structopt::{clap::AppSettings, StructOpt};
use wasi_common::preopen_dir;
use wasmtime::{Engine, Instance, Module, Store};
use wasmtime::{Engine, Instance, Module, Store, Trap};
use wasmtime_interface_types::ModuleData;
use wasmtime_wasi::{old::snapshot_0::Wasi as WasiSnapshot0, Wasi};

Expand Down Expand Up @@ -113,8 +114,31 @@ impl RunCommand {
}

// Load the main wasm module.
self.handle_module(&store, &module_registry)
.with_context(|| format!("failed to run main module `{}`", self.module.display()))?;
match self
.handle_module(&store, &module_registry)
.with_context(|| format!("failed to run main module `{}`", self.module.display()))
{
Ok(()) => (),
Err(e) => {
// If the program exited because of a trap, return an error code
// to the outside environment indicating a more severe problem
// than a simple failure.
if e.is::<Trap>() {
// Print the error message in the usual way.
eprintln!("Error: {:?}", e);

if cfg!(unix) {
// On Unix, return the error code of an abort.
process::exit(128 + libc::SIGABRT);
} else if cfg!(windows) {
// On Windows, return 3.
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
process::exit(3);
}
}
return Err(e);
}
}

Ok(())
}
Expand Down
37 changes: 34 additions & 3 deletions tests/cli_tests.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
use anyhow::{bail, Result};
use std::io::Write;
use std::path::Path;
use std::process::Command;
use std::process::{Command, Output};
use tempfile::NamedTempFile;

fn run_wasmtime(args: &[&str]) -> Result<String> {
// Run the wasmtime CLI with the provided args and return the `Output`.
fn run_wasmtime_for_output(args: &[&str]) -> Result<Output> {
let mut me = std::env::current_exe()?;
me.pop(); // chop off the file name
me.pop(); // chop off `deps`
me.push("wasmtime");
let output = Command::new(&me).args(args).output()?;
Command::new(&me).args(args).output().map_err(Into::into)
}

// Run the wasmtime CLI with the provided args and, if it succeeds, return
// the standard output in a `String`.
fn run_wasmtime(args: &[&str]) -> Result<String> {
let output = run_wasmtime_for_output(args)?;
if !output.status.success() {
bail!(
"Failed to execute wasmtime with: {:?}\n{}",
Expand Down Expand Up @@ -74,3 +81,27 @@ fn run_wasmtime_simple_wat() -> Result<()> {
])?;
Ok(())
}

// Running a wat that traps.
#[test]
fn run_wasmtime_unreachable_wat() -> Result<()> {
let wasm = build_wasm("tests/wasm/unreachable.wat")?;
let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "--disable-cache"])?;

assert_ne!(output.stderr, b"");
assert_eq!(output.stdout, b"");
assert!(!output.status.success());

let code = output
.status
.code()
.expect("wasmtime process should exit normally");

// Test for the specific error code Wasmtime uses to indicate a trap return.
#[cfg(unix)]
assert_eq!(code, 128 + libc::SIGABRT);
#[cfg(windows)]
assert_eq!(code, 3);

Ok(())
}
5 changes: 5 additions & 0 deletions tests/wasm/unreachable.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(module
(func (export "_start")
unreachable
)
)