Skip to content

Commit fb0b9e3

Browse files
authored
Change proc_exit to unwind the stack rather than exiting the host process. (#1646)
* Remove Cranelift's OutOfBounds trap, which is no longer used. * Change proc_exit to unwind instead of exit the host process. This implements the semantics in WebAssembly/WASI#235. Fixes #783. Fixes #993. * Fix exit-status tests on Windows. * Revert the wiggle changes and re-introduce the wasi-common implementations. * Move `wasi_proc_exit` into the wasmtime-wasi crate. * Revert the spec_testsuite change. * Remove the old proc_exit implementations. * Make `TrapReason` an implementation detail. * Allow exit status 2 on Windows too. * Fix a documentation link. * Really fix a documentation link.
1 parent 08983bf commit fb0b9e3

File tree

27 files changed

+268
-64
lines changed

27 files changed

+268
-64
lines changed

cranelift/codegen/src/ir/trapcode.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ pub enum TrapCode {
2727
/// A `table_addr` instruction detected an out-of-bounds error.
2828
TableOutOfBounds,
2929

30-
/// Other bounds checking error.
31-
OutOfBounds,
32-
3330
/// Indirect call to a null table entry.
3431
IndirectCallToNull,
3532

@@ -63,7 +60,6 @@ impl Display for TrapCode {
6360
StackOverflow => "stk_ovf",
6461
HeapOutOfBounds => "heap_oob",
6562
TableOutOfBounds => "table_oob",
66-
OutOfBounds => "oob",
6763
IndirectCallToNull => "icall_null",
6864
BadSignature => "bad_sig",
6965
IntegerOverflow => "int_ovf",
@@ -86,7 +82,6 @@ impl FromStr for TrapCode {
8682
"stk_ovf" => Ok(StackOverflow),
8783
"heap_oob" => Ok(HeapOutOfBounds),
8884
"table_oob" => Ok(TableOutOfBounds),
89-
"oob" => Ok(OutOfBounds),
9085
"icall_null" => Ok(IndirectCallToNull),
9186
"bad_sig" => Ok(BadSignature),
9287
"int_ovf" => Ok(IntegerOverflow),
@@ -106,11 +101,10 @@ mod tests {
106101
use alloc::string::ToString;
107102

108103
// Everything but user-defined codes.
109-
const CODES: [TrapCode; 11] = [
104+
const CODES: [TrapCode; 10] = [
110105
TrapCode::StackOverflow,
111106
TrapCode::HeapOutOfBounds,
112107
TrapCode::TableOutOfBounds,
113-
TrapCode::OutOfBounds,
114108
TrapCode::IndirectCallToNull,
115109
TrapCode::BadSignature,
116110
TrapCode::IntegerOverflow,

cranelift/filetests/filetests/parser/flags.clif

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ block201:
2525
return
2626

2727
block202:
28-
trap oob
28+
trap heap_oob
2929
}
3030
; check: v1 = ifcmp_imm v0, 17
3131
; check: brif eq v1, block201
@@ -56,7 +56,7 @@ block201:
5656
return
5757

5858
block202:
59-
trap oob
59+
trap heap_oob
6060
}
6161
; check: v2 = ffcmp v0, v1
6262
; check: brff eq v2, block201

cranelift/filetests/filetests/parser/tiny.clif

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ function %cond_traps(i32) {
223223
block0(v0: i32):
224224
trapz v0, stk_ovf
225225
v1 = ifcmp_imm v0, 5
226-
trapif ugt v1, oob
226+
trapif ugt v1, heap_oob
227227
v2 = bitcast.f32 v1
228228
v3 = ffcmp v2, v2
229229
trapff uno v3, int_ovf
@@ -233,7 +233,7 @@ block0(v0: i32):
233233
; nextln: block0(v0: i32):
234234
; nextln: trapz v0, stk_ovf
235235
; nextln: v1 = ifcmp_imm v0, 5
236-
; nextln: trapif ugt v1, oob
236+
; nextln: trapif ugt v1, heap_oob
237237
; nextln: v2 = bitcast.f32 v1
238238
; nextln: v3 = ffcmp v2, v2
239239
; nextln: trapff uno v3, int_ovf

cranelift/filetests/filetests/regalloc/iterate.clif

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ block0(v0: i64):
139139
jump block4
140140

141141
block4:
142-
trap oob
142+
trap heap_oob
143143

144144
block2:
145145
v8 = load.i64 v5+8

cranelift/filetests/filetests/wasm/control.clif

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ block0(v0: i32):
5454
br_table v0, block4, jt0
5555

5656
block4:
57-
trap oob
57+
trap heap_oob
5858

5959
block1:
6060
return

crates/wasi-common/src/old/snapshot_0/hostcalls_impl/misc.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -342,13 +342,6 @@ pub(crate) struct FdEventData<'a> {
342342
pub(crate) userdata: wasi::__wasi_userdata_t,
343343
}
344344

345-
pub(crate) fn proc_exit(_wasi_ctx: &WasiCtx, _memory: &mut [u8], rval: wasi::__wasi_exitcode_t) {
346-
trace!("proc_exit(rval={:?})", rval);
347-
// TODO: Rather than call std::process::exit here, we should trigger a
348-
// stack unwind similar to a trap.
349-
std::process::exit(rval as i32);
350-
}
351-
352345
pub(crate) fn proc_raise(
353346
_wasi_ctx: &WasiCtx,
354347
_memory: &mut [u8],

crates/wasi-common/src/snapshots/wasi_snapshot_preview1.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -813,13 +813,10 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
813813
Ok(nevents)
814814
}
815815

816-
// This is just a temporary to ignore the warning which becomes a hard error
817-
// in the CI. Once we figure out non-returns in `wiggle`, this should be gone.
818-
#[allow(unreachable_code)]
819-
fn proc_exit(&self, rval: types::Exitcode) -> std::result::Result<(), ()> {
820-
// TODO: Rather than call std::process::exit here, we should trigger a
821-
// stack unwind similar to a trap.
822-
std::process::exit(rval as i32);
816+
fn proc_exit(&self, _rval: types::Exitcode) -> std::result::Result<(), ()> {
817+
// proc_exit is special in that it's expected to unwind the stack, which
818+
// typically requires runtime-specific logic.
819+
unimplemented!("runtimes are expected to override this implementation")
823820
}
824821

825822
fn proc_raise(&self, _sig: types::Signal) -> Result<()> {

crates/wasi-common/wig/src/hostcalls.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ pub fn define(args: TokenStream) -> TokenStream {
1717

1818
for module in doc.modules() {
1919
for func in module.funcs() {
20+
// `proc_exit` is special; it's essentially an unwinding primitive,
21+
// so we implement it in the runtime rather than use the implementation
22+
// in wasi-common.
23+
if func.name.as_str() == "proc_exit" {
24+
continue;
25+
}
26+
2027
ret.extend(generate_wrappers(&func, old));
2128
}
2229
}

crates/wasi-common/wig/src/wasi.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ pub fn define_struct(args: TokenStream) -> TokenStream {
4646
linker_add.push(quote! {
4747
linker.define(#module_name, #name, self.#name_ident.clone())?;
4848
});
49+
// `proc_exit` is special; it's essentially an unwinding primitive,
50+
// so we implement it in the runtime rather than use the implementation
51+
// in wasi-common.
52+
if name == "proc_exit" {
53+
ctor_externs.push(quote! {
54+
let #name_ident = wasmtime::Func::wrap(store, crate::wasi_proc_exit);
55+
});
56+
continue;
57+
}
4958

5059
let mut shim_arg_decls = Vec::new();
5160
let mut params = Vec::new();
@@ -291,6 +300,15 @@ pub fn define_struct_for_wiggle(args: TokenStream) -> TokenStream {
291300
linker_add.push(quote! {
292301
linker.define(#module_name, #name, self.#name_ident.clone())?;
293302
});
303+
// `proc_exit` is special; it's essentially an unwinding primitive,
304+
// so we implement it in the runtime rather than use the implementation
305+
// in wasi-common.
306+
if name == "proc_exit" {
307+
ctor_externs.push(quote! {
308+
let #name_ident = wasmtime::Func::wrap(store, crate::wasi_proc_exit);
309+
});
310+
continue;
311+
}
294312

295313
let mut shim_arg_decls = Vec::new();
296314
let mut params = Vec::new();

crates/wasi/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use wasmtime::Trap;
2+
13
pub mod old;
24

35
pub use wasi_common::{WasiCtx, WasiCtxBuilder};
@@ -12,3 +14,17 @@ pub fn is_wasi_module(name: &str) -> bool {
1214
// trick.
1315
name.starts_with("wasi")
1416
}
17+
18+
/// Implement the WASI `proc_exit` function. This function is implemented here
19+
/// instead of in wasi-common so that we can use the runtime to perform an
20+
/// unwind rather than exiting the host process.
21+
fn wasi_proc_exit(status: i32) -> Result<(), Trap> {
22+
// Check that the status is within WASI's range.
23+
if status >= 0 && status < 126 {
24+
Err(Trap::i32_exit(status))
25+
} else {
26+
Err(Trap::new(
27+
"exit with invalid exit status outside of [0..126)",
28+
))
29+
}
30+
}

0 commit comments

Comments
 (0)