Skip to content

Commit abdd191

Browse files
committed
Don't leak when panics/traps happen
1 parent bc689cd commit abdd191

File tree

1 file changed

+44
-32
lines changed

1 file changed

+44
-32
lines changed

crates/api/src/trampoline/func.rs

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use super::create_handle::create_handle;
44
use super::trap::TrapSink;
5-
use crate::{Callable, FuncType, Store, Val};
5+
use crate::{Callable, FuncType, Store, Trap, Val};
66
use anyhow::{bail, Result};
77
use std::cmp;
88
use std::convert::TryFrom;
@@ -71,43 +71,21 @@ unsafe extern "C" fn stub_fn(
7171
call_id: u32,
7272
values_vec: *mut i128,
7373
) {
74-
let mut instance = InstanceHandle::from_vmctx(vmctx);
75-
76-
let (args, returns_len) = {
77-
let module = instance.module_ref();
78-
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
79-
80-
let mut args = Vec::new();
81-
for i in 2..signature.params.len() {
82-
args.push(Val::read_value_from(
83-
values_vec.offset(i as isize - 2),
84-
signature.params[i].value_type,
85-
))
86-
}
87-
(args, signature.returns.len())
88-
};
89-
90-
let mut returns = vec![Val::null(); returns_len];
91-
let state = &instance
92-
.host_state()
93-
.downcast_mut::<TrampolineState>()
94-
.expect("state");
95-
9674
// Be sure to `catch_unwind` here in case our callable panics, and if so we
9775
// need to manually carry the panic across the JIT frames back to the
9876
// original call-site of wasm code. Note that eventually we may be able to
9977
// JIT code such that native unwinding can unwind through those frames, but
10078
// for now it's just easier to always catch panics.
101-
let result = panic::catch_unwind(AssertUnwindSafe(|| state.func.call(&args, &mut returns)));
79+
//
80+
// Also note that there are intentionally no local variables on this stack
81+
// frame. The reason for that is that some of the "raise" functions we have
82+
// below will trigger a longjmp, which won't run local destructors if we
83+
// have any. To prevent leaks we avoid having any local destructors by
84+
// avoiding local variables.
85+
let result = panic::catch_unwind(AssertUnwindSafe(|| call_stub(vmctx, call_id, values_vec)));
86+
10287
match result {
103-
// On success we write out all the results into the return pointer that
104-
// we were given.
105-
Ok(Ok(())) => {
106-
for (i, r#return) in returns.iter_mut().enumerate() {
107-
// TODO check signature.returns[i].value_type ?
108-
r#return.write_value_to(values_vec.add(i));
109-
}
110-
}
88+
Ok(Ok(())) => {}
11189

11290
// If a trap was raised (an error returned from the imported function)
11391
// then we smuggle the trap through `Box<dyn Error>` through to the
@@ -121,6 +99,40 @@ unsafe extern "C" fn stub_fn(
12199
// platforms.
122100
Err(panic) => wasmtime_runtime::resume_panic(panic),
123101
}
102+
103+
unsafe fn call_stub(
104+
vmctx: *mut VMContext,
105+
call_id: u32,
106+
values_vec: *mut i128,
107+
) -> Result<(), Trap> {
108+
let mut instance = InstanceHandle::from_vmctx(vmctx);
109+
110+
let (args, returns_len) = {
111+
let module = instance.module_ref();
112+
let signature = &module.signatures[module.functions[FuncIndex::new(call_id as usize)]];
113+
114+
let mut args = Vec::new();
115+
for i in 2..signature.params.len() {
116+
args.push(Val::read_value_from(
117+
values_vec.offset(i as isize - 2),
118+
signature.params[i].value_type,
119+
))
120+
}
121+
(args, signature.returns.len())
122+
};
123+
124+
let mut returns = vec![Val::null(); returns_len];
125+
let state = &instance
126+
.host_state()
127+
.downcast_mut::<TrampolineState>()
128+
.expect("state");
129+
state.func.call(&args, &mut returns)?;
130+
for (i, r#return) in returns.iter_mut().enumerate() {
131+
// TODO check signature.returns[i].value_type ?
132+
r#return.write_value_to(values_vec.add(i));
133+
}
134+
Ok(())
135+
}
124136
}
125137

126138
/// Create a trampoline for invoking a Callable.

0 commit comments

Comments
 (0)