Skip to content

Commit d44384d

Browse files
authored
Exit with a more severe error code if the program traps. (#1274)
* Exit with a more severe error code if the program traps. Previously, the wasmtime CLI would return with a regular failure error code, such as 1 on Unix. However, a program trap indicates a bug in the program, which can be useful to distinguish from a simple error status. Check for the trap case, and return an appropriate OS-specific exit status. * Use a loop to iterate over the error causes to find Traps. * Use anyhow's `chain()` iterator. * For completeness, handle non-Unix and non-Windows platforms too. * Add a CLI test for a trapping program. * Replace a manual `.cause` loop with a `.is` call. * Correct the expected exit status on Windows. * Use assert_eq/assert_ne so that if these fail, it prints the output.
1 parent 81d9a5e commit d44384d

File tree

3 files changed

+66
-6
lines changed

3 files changed

+66
-6
lines changed

src/commands/run.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ use std::{
66
ffi::{OsStr, OsString},
77
fs::File,
88
path::{Component, Path, PathBuf},
9+
process,
910
};
1011
use structopt::{clap::AppSettings, StructOpt};
1112
use wasi_common::preopen_dir;
12-
use wasmtime::{Engine, Instance, Module, Store};
13+
use wasmtime::{Engine, Instance, Module, Store, Trap};
1314
use wasmtime_interface_types::ModuleData;
1415
use wasmtime_wasi::{old::snapshot_0::Wasi as WasiSnapshot0, Wasi};
1516

@@ -113,8 +114,31 @@ impl RunCommand {
113114
}
114115

115116
// Load the main wasm module.
116-
self.handle_module(&store, &module_registry)
117-
.with_context(|| format!("failed to run main module `{}`", self.module.display()))?;
117+
match self
118+
.handle_module(&store, &module_registry)
119+
.with_context(|| format!("failed to run main module `{}`", self.module.display()))
120+
{
121+
Ok(()) => (),
122+
Err(e) => {
123+
// If the program exited because of a trap, return an error code
124+
// to the outside environment indicating a more severe problem
125+
// than a simple failure.
126+
if e.is::<Trap>() {
127+
// Print the error message in the usual way.
128+
eprintln!("Error: {:?}", e);
129+
130+
if cfg!(unix) {
131+
// On Unix, return the error code of an abort.
132+
process::exit(128 + libc::SIGABRT);
133+
} else if cfg!(windows) {
134+
// On Windows, return 3.
135+
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/abort?view=vs-2019
136+
process::exit(3);
137+
}
138+
}
139+
return Err(e);
140+
}
141+
}
118142

119143
Ok(())
120144
}

tests/cli_tests.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
use anyhow::{bail, Result};
22
use std::io::Write;
33
use std::path::Path;
4-
use std::process::Command;
4+
use std::process::{Command, Output};
55
use tempfile::NamedTempFile;
66

7-
fn run_wasmtime(args: &[&str]) -> Result<String> {
7+
// Run the wasmtime CLI with the provided args and return the `Output`.
8+
fn run_wasmtime_for_output(args: &[&str]) -> Result<Output> {
89
let mut me = std::env::current_exe()?;
910
me.pop(); // chop off the file name
1011
me.pop(); // chop off `deps`
1112
me.push("wasmtime");
12-
let output = Command::new(&me).args(args).output()?;
13+
Command::new(&me).args(args).output().map_err(Into::into)
14+
}
15+
16+
// Run the wasmtime CLI with the provided args and, if it succeeds, return
17+
// the standard output in a `String`.
18+
fn run_wasmtime(args: &[&str]) -> Result<String> {
19+
let output = run_wasmtime_for_output(args)?;
1320
if !output.status.success() {
1421
bail!(
1522
"Failed to execute wasmtime with: {:?}\n{}",
@@ -74,3 +81,27 @@ fn run_wasmtime_simple_wat() -> Result<()> {
7481
])?;
7582
Ok(())
7683
}
84+
85+
// Running a wat that traps.
86+
#[test]
87+
fn run_wasmtime_unreachable_wat() -> Result<()> {
88+
let wasm = build_wasm("tests/wasm/unreachable.wat")?;
89+
let output = run_wasmtime_for_output(&[wasm.path().to_str().unwrap(), "--disable-cache"])?;
90+
91+
assert_ne!(output.stderr, b"");
92+
assert_eq!(output.stdout, b"");
93+
assert!(!output.status.success());
94+
95+
let code = output
96+
.status
97+
.code()
98+
.expect("wasmtime process should exit normally");
99+
100+
// Test for the specific error code Wasmtime uses to indicate a trap return.
101+
#[cfg(unix)]
102+
assert_eq!(code, 128 + libc::SIGABRT);
103+
#[cfg(windows)]
104+
assert_eq!(code, 3);
105+
106+
Ok(())
107+
}

tests/wasm/unreachable.wat

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(module
2+
(func (export "_start")
3+
unreachable
4+
)
5+
)

0 commit comments

Comments
 (0)