You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

341 lines
12 KiB

#[cfg(any(
target_os = "linux",
all(target_os = "macos", feature = "posix-signals-on-macos")
))]
mod tests {
use anyhow::Result;
use rustix::io::{mprotect, MprotectFlags};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
Don't try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally "handle" non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it'd longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won't get handled and we'll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and "is this a jit address" closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I'm not entirely certain the purpose of the custom signal handler, though, so I'd look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don't accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
use wasmtime::unix::StoreExt;
use wasmtime::*;
const WAT1: &str = r#"
(module
(func $hostcall_read (import "" "hostcall_read") (result i32))
(func $read (export "read") (result i32)
(i32.load (i32.const 0))
)
(func $read_out_of_bounds (export "read_out_of_bounds") (result i32)
(i32.load
(i32.mul
;; memory size in Wasm pages
(memory.size)
;; Wasm page size
(i32.const 65536)
)
)
)
(func (export "hostcall_read") (result i32)
call $hostcall_read
)
(func $start
(i32.store (i32.const 0) (i32.const 123))
)
(start $start)
(memory (export "memory") 1 4)
)
"#;
const WAT2: &str = r#"
(module
(import "other_module" "read" (func $other_module.read (result i32)))
(func $run (export "run") (result i32)
call $other_module.read)
)
"#;
fn invoke_export(store: &mut Store<()>, instance: Instance, func_name: &str) -> Result<i32> {
let ret = instance
.get_typed_func::<(), i32, _>(&mut *store, func_name)?
.call(store, ())?;
Ok(ret)
}
// Locate "memory" export, get base address and size and set memory protection to PROT_NONE
fn set_up_memory(store: &mut Store<()>, instance: Instance) -> (usize, usize) {
let mem_export = instance.get_memory(&mut *store, "memory").unwrap();
let base = mem_export.data_ptr(&store);
let length = mem_export.data_size(&store);
// So we can later trigger SIGSEGV by performing a read
unsafe {
mprotect(
base as *mut std::ffi::c_void,
length,
MprotectFlags::empty(),
)
.unwrap();
}
println!("memory: base={:?}, length={}", base, length);
(base as usize, length)
}
fn handle_sigsegv(
base: usize,
length: usize,
signum: libc::c_int,
siginfo: *const libc::siginfo_t,
) -> bool {
println!("Hello from instance signal handler!");
// SIGSEGV on Linux, SIGBUS on Mac
if libc::SIGSEGV == signum || libc::SIGBUS == signum {
let si_addr: *mut libc::c_void = unsafe { (*siginfo).si_addr() };
// Any signal from within module's memory we handle ourselves
let result = (si_addr as u64) < (base as u64) + (length as u64);
// Remove protections so the execution may resume
unsafe {
mprotect(
base as *mut libc::c_void,
length,
MprotectFlags::READ | MprotectFlags::WRITE,
)
.unwrap();
}
println!("signal handled: {}", result);
result
} else {
// Otherwise, we forward to wasmtime's signal handler.
false
}
}
fn make_externs(store: &mut Store<()>, module: &Module) -> Vec<Extern> {
module
.imports()
.map(|import| {
assert_eq!(Some("hostcall_read"), import.name());
let func = Func::wrap(&mut *store, {
move |mut caller: Caller<'_, _>| {
let mem = caller.get_export("memory").unwrap().into_memory().unwrap();
let memory = mem.data(&caller);
use std::convert::TryInto;
i32::from_le_bytes(memory[0..4].try_into().unwrap())
}
});
wasmtime::Extern::Func(func)
})
.collect::<Vec<_>>()
}
// This test will only succeed if the SIGSEGV signal originating from the
// hostcall can be handled.
#[test]
fn test_custom_signal_handler_single_instance_hostcall() -> Result<()> {
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, WAT1)?;
let externs = make_externs(&mut store, &module);
let instance = Instance::new(&mut store, &module, &externs)?;
let (base, length) = set_up_memory(&mut store, instance);
unsafe {
store.set_signal_handler(move |signum, siginfo, _| {
handle_sigsegv(base, length, signum, siginfo)
});
}
println!("calling hostcall_read...");
let result = invoke_export(&mut store, instance, "hostcall_read").unwrap();
Redo the statically typed `Func` API (#2719) * Redo the statically typed `Func` API This commit reimplements the `Func` API with respect to statically typed dispatch. Previously `Func` had a `getN` and `getN_async` family of methods which were implemented for 0 to 16 parameters. The return value of these functions was an `impl Fn(..)` closure with the appropriate parameters and return values. There are a number of downsides with this approach that have become apparent over time: * The addition of `*_async` doubled the API surface area (which is quite large here due to one-method-per-number-of-parameters). * The [documentation of `Func`][old-docs] are quite verbose and feel &#34;polluted&#34; with all these getters, making it harder to understand the other methods that can be used to interact with a `Func`. * These methods unconditionally pay the cost of returning an owned `impl Fn` with a `&#39;static` lifetime. While cheap, this is still paying the cost for cloning the `Store` effectively and moving data into the closed-over environment. * Storage of the return value into a struct, for example, always requires `Box`-ing the returned closure since it otherwise cannot be named. * Recently I had the desire to implement an &#34;unchecked&#34; path for invoking wasm where you unsafely assert the type signature of a wasm function. Doing this with today&#39;s scheme would require doubling (again) the API surface area for both async and synchronous calls, further polluting the documentation. The main benefit of the previous scheme is that by returning a `impl Fn` it was quite easy and ergonomic to actually invoke the function. In practice, though, examples would often have something akin to `.get0::&lt;()&gt;()?()?` which is a lot of things to interpret all at once. Note that `get0` means &#34;0 parameters&#34; yet a type parameter is passed. There&#39;s also a double function invocation which looks like a lot of characters all lined up in a row. Overall, I think that the previous design is starting to show too many cracks and deserves a rewrite. This commit is that rewrite. The new design in this commit is to delete the `getN{,_async}` family of functions and instead have a new API: impl Func { fn typed&lt;P, R&gt;(&amp;self) -&gt; Result&lt;&amp;Typed&lt;P, R&gt;&gt;; } impl Typed&lt;P, R&gt; { fn call(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; async fn call_async(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; } This should entirely replace the current scheme, albeit by slightly losing ergonomics use cases. The idea behind the API is that the existence of `Typed&lt;P, R&gt;` is a &#34;proof&#34; that the underlying function takes `P` and returns `R`. The `Func::typed` method peforms a runtime type-check to ensure that types all match up, and if successful you get a `Typed` value. Otherwise an error is returned. Once you have a `Typed` then, like `Func`, you can either `call` or `call_async`. The difference with a `Typed`, however, is that the params/results are statically known and hence these calls can be much more efficient. This is a much smaller API surface area from before and should greatly simplify the `Func` documentation. There&#39;s still a problem where `Func::wrapN_async` produces a lot of functions to document, but that&#39;s now the sole offender. It&#39;s a nice benefit that the statically-typed-async verisons are now expressed with an `async` function rather than a function-returning-a-future which makes it both more efficient and easier to understand. The type `P` and `R` are intended to either be bare types (e.g. `i32`) or tuples of any length (including 0). At this time `R` is only allowed to be `()` or a bare `i32`-style type because multi-value is not supported with a native ABI (yet). The `P`, however, can be any size of tuples of parameters. This is also where some ergonomics are lost because instead of `f(1, 2)` you now have to write `f.call((1, 2))` (note the double-parens). Similarly `f()` becomes `f.call(())`. Overall I feel that this is a better tradeoff than before. While not universally better due to the loss in ergonomics I feel that this design is much more flexible in terms of what you can do with the return value and also understanding the API surface area (just less to take in). [old-docs]: https://docs.rs/wasmtime/0.24.0/wasmtime/struct.Func.html#method.get0 * Rename Typed to TypedFunc * Implement multi-value returns through `Func::typed` * Fix examples in docs * Fix some more errors * More test fixes * Rebasing and adding `get_typed_func` * Updating tests * Fix typo * More doc tweaks * Tweak visibility on `Func::invoke` * Fix tests again
4 years ago
assert_eq!(123, result);
Ok(())
}
#[test]
fn test_custom_signal_handler_single_instance() -> Result<()> {
Implement shared host functions. (#2625) * Implement defining host functions at the Config level. This commit introduces defining host functions at the `Config` rather than with `Func` tied to a `Store`. The intention here is to enable a host to define all of the functions once with a `Config` and then use a `Linker` (or directly with `Store::get_host_func`) to use the functions when instantiating a module. This should help improve the performance of use cases where a `Store` is short-lived and redefining the functions at every module instantiation is a noticeable performance hit. This commit adds `add_to_config` to the code generation for Wasmtime&#39;s `Wasi` type. The new method adds the WASI functions to the given config as host functions. This commit adds context functions to `Store`: `get` to get a context of a particular type and `set` to set the context on the store. For safety, `set` cannot replace an existing context value of the same type. `Wasi::set_context` was added to set the WASI context for a `Store` when using `Wasi::add_to_config`. * Add `Config::define_host_func_async`. * Make config &#34;async&#34; rather than store. This commit moves the concept of &#34;async-ness&#34; to `Config` rather than `Store`. Note: this is a breaking API change for anyone that&#39;s already adopted the new async support in Wasmtime. Now `Config::new_async` is used to create an &#34;async&#34; config and any `Store` associated with that config is inherently &#34;async&#34;. This is needed for async shared host functions to have some sanity check during their execution (async host functions, like &#34;async&#34; `Func`, need to be called with the &#34;async&#34; variants). * Update async function tests to smoke async shared host functions. This commit updates the async function tests to also smoke the shared host functions, plus `Func::wrap0_async`. This also changes the &#34;wrap async&#34; method names on `Config` to `wrap$N_host_func_async` to slightly better match what is on `Func`. * Move the instance allocator into `Engine`. This commit moves the instantiated instance allocator from `Config` into `Engine`. This makes certain settings in `Config` no longer order-dependent, which is how `Config` should ideally be. This also removes the confusing concept of the &#34;default&#34; instance allocator, instead opting to construct the on-demand instance allocator when needed. This does alter the semantics of the instance allocator as now each `Engine` gets its own instance allocator rather than sharing a single one between all engines created from a configuration. * Make `Engine::new` return `Result`. This is a breaking API change for anyone using `Engine::new`. As creating the pooling instance allocator may fail (likely cause is not enough memory for the provided limits), instead of panicking when creating an `Engine`, `Engine::new` now returns a `Result`. * Remove `Config::new_async`. This commit removes `Config::new_async` in favor of treating &#34;async support&#34; as any other setting on `Config`. The setting is `Config::async_support`. * Remove order dependency when defining async host functions in `Config`. This commit removes the order dependency where async support must be enabled on the `Config` prior to defining async host functions. The check is now delayed to when an `Engine` is created from the config. * Update WASI example to use shared `Wasi::add_to_config`. This commit updates the WASI example to use `Wasi::add_to_config`. As only a single store and instance are used in the example, it has no semantic difference from the previous example, but the intention is to steer users towards defining WASI on the config and only using `Wasi::add_to_linker` when more explicit scoping of the WASI context is required.
4 years ago
let engine = Engine::new(&Config::default())?;
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, WAT1)?;
let externs = make_externs(&mut store, &module);
let instance = Instance::new(&mut store, &module, &externs)?;
let (base, length) = set_up_memory(&mut store, instance);
unsafe {
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
store.set_signal_handler(move |signum, siginfo, _| {
handle_sigsegv(base, length, signum, siginfo)
});
}
// these invoke wasmtime_call_trampoline from action.rs
{
println!("calling read...");
let result = invoke_export(&mut store, instance, "read").expect("read succeeded");
Redo the statically typed `Func` API (#2719) * Redo the statically typed `Func` API This commit reimplements the `Func` API with respect to statically typed dispatch. Previously `Func` had a `getN` and `getN_async` family of methods which were implemented for 0 to 16 parameters. The return value of these functions was an `impl Fn(..)` closure with the appropriate parameters and return values. There are a number of downsides with this approach that have become apparent over time: * The addition of `*_async` doubled the API surface area (which is quite large here due to one-method-per-number-of-parameters). * The [documentation of `Func`][old-docs] are quite verbose and feel &#34;polluted&#34; with all these getters, making it harder to understand the other methods that can be used to interact with a `Func`. * These methods unconditionally pay the cost of returning an owned `impl Fn` with a `&#39;static` lifetime. While cheap, this is still paying the cost for cloning the `Store` effectively and moving data into the closed-over environment. * Storage of the return value into a struct, for example, always requires `Box`-ing the returned closure since it otherwise cannot be named. * Recently I had the desire to implement an &#34;unchecked&#34; path for invoking wasm where you unsafely assert the type signature of a wasm function. Doing this with today&#39;s scheme would require doubling (again) the API surface area for both async and synchronous calls, further polluting the documentation. The main benefit of the previous scheme is that by returning a `impl Fn` it was quite easy and ergonomic to actually invoke the function. In practice, though, examples would often have something akin to `.get0::&lt;()&gt;()?()?` which is a lot of things to interpret all at once. Note that `get0` means &#34;0 parameters&#34; yet a type parameter is passed. There&#39;s also a double function invocation which looks like a lot of characters all lined up in a row. Overall, I think that the previous design is starting to show too many cracks and deserves a rewrite. This commit is that rewrite. The new design in this commit is to delete the `getN{,_async}` family of functions and instead have a new API: impl Func { fn typed&lt;P, R&gt;(&amp;self) -&gt; Result&lt;&amp;Typed&lt;P, R&gt;&gt;; } impl Typed&lt;P, R&gt; { fn call(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; async fn call_async(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; } This should entirely replace the current scheme, albeit by slightly losing ergonomics use cases. The idea behind the API is that the existence of `Typed&lt;P, R&gt;` is a &#34;proof&#34; that the underlying function takes `P` and returns `R`. The `Func::typed` method peforms a runtime type-check to ensure that types all match up, and if successful you get a `Typed` value. Otherwise an error is returned. Once you have a `Typed` then, like `Func`, you can either `call` or `call_async`. The difference with a `Typed`, however, is that the params/results are statically known and hence these calls can be much more efficient. This is a much smaller API surface area from before and should greatly simplify the `Func` documentation. There&#39;s still a problem where `Func::wrapN_async` produces a lot of functions to document, but that&#39;s now the sole offender. It&#39;s a nice benefit that the statically-typed-async verisons are now expressed with an `async` function rather than a function-returning-a-future which makes it both more efficient and easier to understand. The type `P` and `R` are intended to either be bare types (e.g. `i32`) or tuples of any length (including 0). At this time `R` is only allowed to be `()` or a bare `i32`-style type because multi-value is not supported with a native ABI (yet). The `P`, however, can be any size of tuples of parameters. This is also where some ergonomics are lost because instead of `f(1, 2)` you now have to write `f.call((1, 2))` (note the double-parens). Similarly `f()` becomes `f.call(())`. Overall I feel that this is a better tradeoff than before. While not universally better due to the loss in ergonomics I feel that this design is much more flexible in terms of what you can do with the return value and also understanding the API surface area (just less to take in). [old-docs]: https://docs.rs/wasmtime/0.24.0/wasmtime/struct.Func.html#method.get0 * Rename Typed to TypedFunc * Implement multi-value returns through `Func::typed` * Fix examples in docs * Fix some more errors * More test fixes * Rebasing and adding `get_typed_func` * Updating tests * Fix typo * More doc tweaks * Tweak visibility on `Func::invoke` * Fix tests again
4 years ago
assert_eq!(123, result);
}
{
println!("calling read_out_of_bounds...");
let trap = invoke_export(&mut store, instance, "read_out_of_bounds")
.unwrap_err()
.downcast::<Trap>()?;
assert!(
trap.to_string()
.contains("wasm trap: out of bounds memory access"),
"bad trap message: {:?}",
trap.to_string()
);
}
// these invoke wasmtime_call_trampoline from callable.rs
{
let read_func = instance.get_typed_func::<(), i32, _>(&mut store, "read")?;
println!("calling read...");
let result = read_func
.call(&mut store, ())
.expect("expected function not to trap");
Redo the statically typed `Func` API (#2719) * Redo the statically typed `Func` API This commit reimplements the `Func` API with respect to statically typed dispatch. Previously `Func` had a `getN` and `getN_async` family of methods which were implemented for 0 to 16 parameters. The return value of these functions was an `impl Fn(..)` closure with the appropriate parameters and return values. There are a number of downsides with this approach that have become apparent over time: * The addition of `*_async` doubled the API surface area (which is quite large here due to one-method-per-number-of-parameters). * The [documentation of `Func`][old-docs] are quite verbose and feel &#34;polluted&#34; with all these getters, making it harder to understand the other methods that can be used to interact with a `Func`. * These methods unconditionally pay the cost of returning an owned `impl Fn` with a `&#39;static` lifetime. While cheap, this is still paying the cost for cloning the `Store` effectively and moving data into the closed-over environment. * Storage of the return value into a struct, for example, always requires `Box`-ing the returned closure since it otherwise cannot be named. * Recently I had the desire to implement an &#34;unchecked&#34; path for invoking wasm where you unsafely assert the type signature of a wasm function. Doing this with today&#39;s scheme would require doubling (again) the API surface area for both async and synchronous calls, further polluting the documentation. The main benefit of the previous scheme is that by returning a `impl Fn` it was quite easy and ergonomic to actually invoke the function. In practice, though, examples would often have something akin to `.get0::&lt;()&gt;()?()?` which is a lot of things to interpret all at once. Note that `get0` means &#34;0 parameters&#34; yet a type parameter is passed. There&#39;s also a double function invocation which looks like a lot of characters all lined up in a row. Overall, I think that the previous design is starting to show too many cracks and deserves a rewrite. This commit is that rewrite. The new design in this commit is to delete the `getN{,_async}` family of functions and instead have a new API: impl Func { fn typed&lt;P, R&gt;(&amp;self) -&gt; Result&lt;&amp;Typed&lt;P, R&gt;&gt;; } impl Typed&lt;P, R&gt; { fn call(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; async fn call_async(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; } This should entirely replace the current scheme, albeit by slightly losing ergonomics use cases. The idea behind the API is that the existence of `Typed&lt;P, R&gt;` is a &#34;proof&#34; that the underlying function takes `P` and returns `R`. The `Func::typed` method peforms a runtime type-check to ensure that types all match up, and if successful you get a `Typed` value. Otherwise an error is returned. Once you have a `Typed` then, like `Func`, you can either `call` or `call_async`. The difference with a `Typed`, however, is that the params/results are statically known and hence these calls can be much more efficient. This is a much smaller API surface area from before and should greatly simplify the `Func` documentation. There&#39;s still a problem where `Func::wrapN_async` produces a lot of functions to document, but that&#39;s now the sole offender. It&#39;s a nice benefit that the statically-typed-async verisons are now expressed with an `async` function rather than a function-returning-a-future which makes it both more efficient and easier to understand. The type `P` and `R` are intended to either be bare types (e.g. `i32`) or tuples of any length (including 0). At this time `R` is only allowed to be `()` or a bare `i32`-style type because multi-value is not supported with a native ABI (yet). The `P`, however, can be any size of tuples of parameters. This is also where some ergonomics are lost because instead of `f(1, 2)` you now have to write `f.call((1, 2))` (note the double-parens). Similarly `f()` becomes `f.call(())`. Overall I feel that this is a better tradeoff than before. While not universally better due to the loss in ergonomics I feel that this design is much more flexible in terms of what you can do with the return value and also understanding the API surface area (just less to take in). [old-docs]: https://docs.rs/wasmtime/0.24.0/wasmtime/struct.Func.html#method.get0 * Rename Typed to TypedFunc * Implement multi-value returns through `Func::typed` * Fix examples in docs * Fix some more errors * More test fixes * Rebasing and adding `get_typed_func` * Updating tests * Fix typo * More doc tweaks * Tweak visibility on `Func::invoke` * Fix tests again
4 years ago
assert_eq!(123i32, result);
}
{
Redo the statically typed `Func` API (#2719) * Redo the statically typed `Func` API This commit reimplements the `Func` API with respect to statically typed dispatch. Previously `Func` had a `getN` and `getN_async` family of methods which were implemented for 0 to 16 parameters. The return value of these functions was an `impl Fn(..)` closure with the appropriate parameters and return values. There are a number of downsides with this approach that have become apparent over time: * The addition of `*_async` doubled the API surface area (which is quite large here due to one-method-per-number-of-parameters). * The [documentation of `Func`][old-docs] are quite verbose and feel &#34;polluted&#34; with all these getters, making it harder to understand the other methods that can be used to interact with a `Func`. * These methods unconditionally pay the cost of returning an owned `impl Fn` with a `&#39;static` lifetime. While cheap, this is still paying the cost for cloning the `Store` effectively and moving data into the closed-over environment. * Storage of the return value into a struct, for example, always requires `Box`-ing the returned closure since it otherwise cannot be named. * Recently I had the desire to implement an &#34;unchecked&#34; path for invoking wasm where you unsafely assert the type signature of a wasm function. Doing this with today&#39;s scheme would require doubling (again) the API surface area for both async and synchronous calls, further polluting the documentation. The main benefit of the previous scheme is that by returning a `impl Fn` it was quite easy and ergonomic to actually invoke the function. In practice, though, examples would often have something akin to `.get0::&lt;()&gt;()?()?` which is a lot of things to interpret all at once. Note that `get0` means &#34;0 parameters&#34; yet a type parameter is passed. There&#39;s also a double function invocation which looks like a lot of characters all lined up in a row. Overall, I think that the previous design is starting to show too many cracks and deserves a rewrite. This commit is that rewrite. The new design in this commit is to delete the `getN{,_async}` family of functions and instead have a new API: impl Func { fn typed&lt;P, R&gt;(&amp;self) -&gt; Result&lt;&amp;Typed&lt;P, R&gt;&gt;; } impl Typed&lt;P, R&gt; { fn call(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; async fn call_async(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; } This should entirely replace the current scheme, albeit by slightly losing ergonomics use cases. The idea behind the API is that the existence of `Typed&lt;P, R&gt;` is a &#34;proof&#34; that the underlying function takes `P` and returns `R`. The `Func::typed` method peforms a runtime type-check to ensure that types all match up, and if successful you get a `Typed` value. Otherwise an error is returned. Once you have a `Typed` then, like `Func`, you can either `call` or `call_async`. The difference with a `Typed`, however, is that the params/results are statically known and hence these calls can be much more efficient. This is a much smaller API surface area from before and should greatly simplify the `Func` documentation. There&#39;s still a problem where `Func::wrapN_async` produces a lot of functions to document, but that&#39;s now the sole offender. It&#39;s a nice benefit that the statically-typed-async verisons are now expressed with an `async` function rather than a function-returning-a-future which makes it both more efficient and easier to understand. The type `P` and `R` are intended to either be bare types (e.g. `i32`) or tuples of any length (including 0). At this time `R` is only allowed to be `()` or a bare `i32`-style type because multi-value is not supported with a native ABI (yet). The `P`, however, can be any size of tuples of parameters. This is also where some ergonomics are lost because instead of `f(1, 2)` you now have to write `f.call((1, 2))` (note the double-parens). Similarly `f()` becomes `f.call(())`. Overall I feel that this is a better tradeoff than before. While not universally better due to the loss in ergonomics I feel that this design is much more flexible in terms of what you can do with the return value and also understanding the API surface area (just less to take in). [old-docs]: https://docs.rs/wasmtime/0.24.0/wasmtime/struct.Func.html#method.get0 * Rename Typed to TypedFunc * Implement multi-value returns through `Func::typed` * Fix examples in docs * Fix some more errors * More test fixes * Rebasing and adding `get_typed_func` * Updating tests * Fix typo * More doc tweaks * Tweak visibility on `Func::invoke` * Fix tests again
4 years ago
let read_out_of_bounds_func =
instance.get_typed_func::<(), i32, _>(&mut store, "read_out_of_bounds")?;
println!("calling read_out_of_bounds...");
let trap = read_out_of_bounds_func.call(&mut store, ()).unwrap_err();
assert!(trap
.to_string()
.contains("wasm trap: out of bounds memory access"));
}
Ok(())
}
#[test]
fn test_custom_signal_handler_multiple_instances() -> Result<()> {
let engine = Engine::default();
let mut store = Store::new(&engine, ());
let module = Module::new(&engine, WAT1)?;
// Set up multiple instances
let externs = make_externs(&mut store, &module);
let instance1 = Instance::new(&mut store, &module, &externs)?;
let instance1_handler_triggered = Arc::new(AtomicBool::new(false));
unsafe {
let (base1, length1) = set_up_memory(&mut store, instance1);
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
store.set_signal_handler({
let instance1_handler_triggered = instance1_handler_triggered.clone();
move |_signum, _siginfo, _context| {
// Remove protections so the execution may resume
mprotect(
base1 as *mut libc::c_void,
length1,
MprotectFlags::READ | MprotectFlags::WRITE,
)
.unwrap();
instance1_handler_triggered.store(true, Ordering::SeqCst);
println!(
"Hello from instance1 signal handler! {}",
instance1_handler_triggered.load(Ordering::SeqCst)
);
true
}
});
}
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
// Invoke both instances and trigger both signal handlers
// First instance1
{
let mut exports1 = instance1.exports(&mut store);
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
assert!(exports1.next().is_some());
drop(exports1);
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
println!("calling instance1.read...");
let result = invoke_export(&mut store, instance1, "read").expect("read succeeded");
Redo the statically typed `Func` API (#2719) * Redo the statically typed `Func` API This commit reimplements the `Func` API with respect to statically typed dispatch. Previously `Func` had a `getN` and `getN_async` family of methods which were implemented for 0 to 16 parameters. The return value of these functions was an `impl Fn(..)` closure with the appropriate parameters and return values. There are a number of downsides with this approach that have become apparent over time: * The addition of `*_async` doubled the API surface area (which is quite large here due to one-method-per-number-of-parameters). * The [documentation of `Func`][old-docs] are quite verbose and feel &#34;polluted&#34; with all these getters, making it harder to understand the other methods that can be used to interact with a `Func`. * These methods unconditionally pay the cost of returning an owned `impl Fn` with a `&#39;static` lifetime. While cheap, this is still paying the cost for cloning the `Store` effectively and moving data into the closed-over environment. * Storage of the return value into a struct, for example, always requires `Box`-ing the returned closure since it otherwise cannot be named. * Recently I had the desire to implement an &#34;unchecked&#34; path for invoking wasm where you unsafely assert the type signature of a wasm function. Doing this with today&#39;s scheme would require doubling (again) the API surface area for both async and synchronous calls, further polluting the documentation. The main benefit of the previous scheme is that by returning a `impl Fn` it was quite easy and ergonomic to actually invoke the function. In practice, though, examples would often have something akin to `.get0::&lt;()&gt;()?()?` which is a lot of things to interpret all at once. Note that `get0` means &#34;0 parameters&#34; yet a type parameter is passed. There&#39;s also a double function invocation which looks like a lot of characters all lined up in a row. Overall, I think that the previous design is starting to show too many cracks and deserves a rewrite. This commit is that rewrite. The new design in this commit is to delete the `getN{,_async}` family of functions and instead have a new API: impl Func { fn typed&lt;P, R&gt;(&amp;self) -&gt; Result&lt;&amp;Typed&lt;P, R&gt;&gt;; } impl Typed&lt;P, R&gt; { fn call(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; async fn call_async(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; } This should entirely replace the current scheme, albeit by slightly losing ergonomics use cases. The idea behind the API is that the existence of `Typed&lt;P, R&gt;` is a &#34;proof&#34; that the underlying function takes `P` and returns `R`. The `Func::typed` method peforms a runtime type-check to ensure that types all match up, and if successful you get a `Typed` value. Otherwise an error is returned. Once you have a `Typed` then, like `Func`, you can either `call` or `call_async`. The difference with a `Typed`, however, is that the params/results are statically known and hence these calls can be much more efficient. This is a much smaller API surface area from before and should greatly simplify the `Func` documentation. There&#39;s still a problem where `Func::wrapN_async` produces a lot of functions to document, but that&#39;s now the sole offender. It&#39;s a nice benefit that the statically-typed-async verisons are now expressed with an `async` function rather than a function-returning-a-future which makes it both more efficient and easier to understand. The type `P` and `R` are intended to either be bare types (e.g. `i32`) or tuples of any length (including 0). At this time `R` is only allowed to be `()` or a bare `i32`-style type because multi-value is not supported with a native ABI (yet). The `P`, however, can be any size of tuples of parameters. This is also where some ergonomics are lost because instead of `f(1, 2)` you now have to write `f.call((1, 2))` (note the double-parens). Similarly `f()` becomes `f.call(())`. Overall I feel that this is a better tradeoff than before. While not universally better due to the loss in ergonomics I feel that this design is much more flexible in terms of what you can do with the return value and also understanding the API surface area (just less to take in). [old-docs]: https://docs.rs/wasmtime/0.24.0/wasmtime/struct.Func.html#method.get0 * Rename Typed to TypedFunc * Implement multi-value returns through `Func::typed` * Fix examples in docs * Fix some more errors * More test fixes * Rebasing and adding `get_typed_func` * Updating tests * Fix typo * More doc tweaks * Tweak visibility on `Func::invoke` * Fix tests again
4 years ago
assert_eq!(123, result);
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
assert_eq!(
instance1_handler_triggered.load(Ordering::SeqCst),
true,
"instance1 signal handler has been triggered"
);
}
let externs = make_externs(&mut store, &module);
let instance2 =
Instance::new(&mut store, &module, &externs).expect("failed to instantiate module");
let instance2_handler_triggered = Arc::new(AtomicBool::new(false));
unsafe {
let (base2, length2) = set_up_memory(&mut store, instance2);
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
store.set_signal_handler({
let instance2_handler_triggered = instance2_handler_triggered.clone();
move |_signum, _siginfo, _context| {
// Remove protections so the execution may resume
mprotect(
base2 as *mut libc::c_void,
length2,
MprotectFlags::READ | MprotectFlags::WRITE,
)
.unwrap();
instance2_handler_triggered.store(true, Ordering::SeqCst);
println!(
"Hello from instance2 signal handler! {}",
instance2_handler_triggered.load(Ordering::SeqCst)
);
true
}
});
}
// And then instance2
{
let mut exports2 = instance2.exports(&mut store);
Refactor (#1524) * Compute instance exports on demand. Instead having instances eagerly compute a Vec of Externs, and bumping the refcount for each Extern, compute Externs on demand. This also enables `Instance::get_export` to avoid doing a linear search. This also means that the closure returned by `get0` and friends now holds an `InstanceHandle` to dynamically hold the instance live rather than being scoped to a lifetime. * Compute module imports and exports on demand too. And compute Extern::ty on demand too. * Add a utility function for computing an ExternType. * Add a utility function for looking up a function&#39;s signature. * Add a utility function for computing the ValType of a Global. * Rename wasmtime_environ::Export to EntityIndex. This helps differentiate it from other Export types in the tree, and describes what it is. * Fix a typo in a comment. * Simplify module imports and exports. * Make `Instance::exports` return the export names. This significantly simplifies the public API, as it&#39;s relatively common to need the names, and this avoids the need to do a zip with `Module::exports`. This also changes `ImportType` and `ExportType` to have public members instead of private members and accessors, as I find that simplifies the usage particularly in cases where there are temporary instances. * Remove `Instance::module`. This doesn&#39;t quite remove `Instance`&#39;s `module` member, it gets a step closer. * Use a InstanceHandle utility function. * Don&#39;t consume self in the `Func::get*` methods. Instead, just create a closure containing the instance handle and the export for them to call. * Use `ExactSizeIterator` to avoid needing separate `num_*` methods. * Rename `Extern::func()` etc. to `into_func()` etc. * Revise examples to avoid using `nth`. * Add convenience methods to instance for getting specific extern types. * Use the convenience functions in more tests and examples. * Avoid cloning strings for `ImportType` and `ExportType`. * Remove more obviated clone() calls. * Simplify `Func`&#39;s closure state. * Make wasmtime::Export&#39;s fields private. This makes them more consistent with ExportType. * Fix compilation error. * Make a lifetime parameter explicit, and use better lifetime names. Instead of &#39;me, use &#39;instance and &#39;module to make it clear what the lifetime is. * More lifetime cleanups.
5 years ago
assert!(exports2.next().is_some());
drop(exports2);
println!("calling instance2.read...");
let result = invoke_export(&mut store, instance2, "read").expect("read succeeded");
Redo the statically typed `Func` API (#2719) * Redo the statically typed `Func` API This commit reimplements the `Func` API with respect to statically typed dispatch. Previously `Func` had a `getN` and `getN_async` family of methods which were implemented for 0 to 16 parameters. The return value of these functions was an `impl Fn(..)` closure with the appropriate parameters and return values. There are a number of downsides with this approach that have become apparent over time: * The addition of `*_async` doubled the API surface area (which is quite large here due to one-method-per-number-of-parameters). * The [documentation of `Func`][old-docs] are quite verbose and feel &#34;polluted&#34; with all these getters, making it harder to understand the other methods that can be used to interact with a `Func`. * These methods unconditionally pay the cost of returning an owned `impl Fn` with a `&#39;static` lifetime. While cheap, this is still paying the cost for cloning the `Store` effectively and moving data into the closed-over environment. * Storage of the return value into a struct, for example, always requires `Box`-ing the returned closure since it otherwise cannot be named. * Recently I had the desire to implement an &#34;unchecked&#34; path for invoking wasm where you unsafely assert the type signature of a wasm function. Doing this with today&#39;s scheme would require doubling (again) the API surface area for both async and synchronous calls, further polluting the documentation. The main benefit of the previous scheme is that by returning a `impl Fn` it was quite easy and ergonomic to actually invoke the function. In practice, though, examples would often have something akin to `.get0::&lt;()&gt;()?()?` which is a lot of things to interpret all at once. Note that `get0` means &#34;0 parameters&#34; yet a type parameter is passed. There&#39;s also a double function invocation which looks like a lot of characters all lined up in a row. Overall, I think that the previous design is starting to show too many cracks and deserves a rewrite. This commit is that rewrite. The new design in this commit is to delete the `getN{,_async}` family of functions and instead have a new API: impl Func { fn typed&lt;P, R&gt;(&amp;self) -&gt; Result&lt;&amp;Typed&lt;P, R&gt;&gt;; } impl Typed&lt;P, R&gt; { fn call(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; async fn call_async(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; } This should entirely replace the current scheme, albeit by slightly losing ergonomics use cases. The idea behind the API is that the existence of `Typed&lt;P, R&gt;` is a &#34;proof&#34; that the underlying function takes `P` and returns `R`. The `Func::typed` method peforms a runtime type-check to ensure that types all match up, and if successful you get a `Typed` value. Otherwise an error is returned. Once you have a `Typed` then, like `Func`, you can either `call` or `call_async`. The difference with a `Typed`, however, is that the params/results are statically known and hence these calls can be much more efficient. This is a much smaller API surface area from before and should greatly simplify the `Func` documentation. There&#39;s still a problem where `Func::wrapN_async` produces a lot of functions to document, but that&#39;s now the sole offender. It&#39;s a nice benefit that the statically-typed-async verisons are now expressed with an `async` function rather than a function-returning-a-future which makes it both more efficient and easier to understand. The type `P` and `R` are intended to either be bare types (e.g. `i32`) or tuples of any length (including 0). At this time `R` is only allowed to be `()` or a bare `i32`-style type because multi-value is not supported with a native ABI (yet). The `P`, however, can be any size of tuples of parameters. This is also where some ergonomics are lost because instead of `f(1, 2)` you now have to write `f.call((1, 2))` (note the double-parens). Similarly `f()` becomes `f.call(())`. Overall I feel that this is a better tradeoff than before. While not universally better due to the loss in ergonomics I feel that this design is much more flexible in terms of what you can do with the return value and also understanding the API surface area (just less to take in). [old-docs]: https://docs.rs/wasmtime/0.24.0/wasmtime/struct.Func.html#method.get0 * Rename Typed to TypedFunc * Implement multi-value returns through `Func::typed` * Fix examples in docs * Fix some more errors * More test fixes * Rebasing and adding `get_typed_func` * Updating tests * Fix typo * More doc tweaks * Tweak visibility on `Func::invoke` * Fix tests again
4 years ago
assert_eq!(123, result);
assert_eq!(
instance2_handler_triggered.load(Ordering::SeqCst),
true,
"instance1 signal handler has been triggered"
);
}
Ok(())
}
#[test]
fn test_custom_signal_handler_instance_calling_another_instance() -> Result<()> {
let engine = Engine::default();
let mut store = Store::new(&engine, ());
// instance1 which defines 'read'
let module1 = Module::new(&engine, WAT1)?;
let externs = make_externs(&mut store, &module1);
let instance1 = Instance::new(&mut store, &module1, &externs)?;
let (base1, length1) = set_up_memory(&mut store, instance1);
unsafe {
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
store.set_signal_handler(move |signum, siginfo, _| {
println!("instance1");
handle_sigsegv(base1, length1, signum, siginfo)
});
}
let mut instance1_exports = instance1.exports(&mut store);
let instance1_read = instance1_exports.next().unwrap().clone().into_extern();
drop(instance1_exports);
Refactor (#1524) * Compute instance exports on demand. Instead having instances eagerly compute a Vec of Externs, and bumping the refcount for each Extern, compute Externs on demand. This also enables `Instance::get_export` to avoid doing a linear search. This also means that the closure returned by `get0` and friends now holds an `InstanceHandle` to dynamically hold the instance live rather than being scoped to a lifetime. * Compute module imports and exports on demand too. And compute Extern::ty on demand too. * Add a utility function for computing an ExternType. * Add a utility function for looking up a function&#39;s signature. * Add a utility function for computing the ValType of a Global. * Rename wasmtime_environ::Export to EntityIndex. This helps differentiate it from other Export types in the tree, and describes what it is. * Fix a typo in a comment. * Simplify module imports and exports. * Make `Instance::exports` return the export names. This significantly simplifies the public API, as it&#39;s relatively common to need the names, and this avoids the need to do a zip with `Module::exports`. This also changes `ImportType` and `ExportType` to have public members instead of private members and accessors, as I find that simplifies the usage particularly in cases where there are temporary instances. * Remove `Instance::module`. This doesn&#39;t quite remove `Instance`&#39;s `module` member, it gets a step closer. * Use a InstanceHandle utility function. * Don&#39;t consume self in the `Func::get*` methods. Instead, just create a closure containing the instance handle and the export for them to call. * Use `ExactSizeIterator` to avoid needing separate `num_*` methods. * Rename `Extern::func()` etc. to `into_func()` etc. * Revise examples to avoid using `nth`. * Add convenience methods to instance for getting specific extern types. * Use the convenience functions in more tests and examples. * Avoid cloning strings for `ImportType` and `ExportType`. * Remove more obviated clone() calls. * Simplify `Func`&#39;s closure state. * Make wasmtime::Export&#39;s fields private. This makes them more consistent with ExportType. * Fix compilation error. * Make a lifetime parameter explicit, and use better lifetime names. Instead of &#39;me, use &#39;instance and &#39;module to make it clear what the lifetime is. * More lifetime cleanups.
5 years ago
// instance2 which calls 'instance1.read'
let module2 = Module::new(&engine, WAT2)?;
let instance2 = Instance::new(&mut store, &module2, &[instance1_read])?;
// since 'instance2.run' calls 'instance1.read' we need to set up the signal handler to handle
// SIGSEGV originating from within the memory of instance1
unsafe {
Don&#39;t try to handle non-wasmtime segfaults (#1577) This commit fixes an issue in Wasmtime where Wasmtime would accidentally &#34;handle&#34; non-wasm segfaults while executing host imports of wasm modules. If a host import segfaulted then Wasmtime would recognize that wasm code is on the stack, so it&#39;d longjmp out of the wasm code. This papers over real bugs though in host code and erroneously classified segfaults as wasm traps. The fix here was to add a check to our wasm signal handler for if the faulting address falls in JIT code itself. Actually threading through all the right information for that check to happen is a bit tricky, though, so this involved some refactoring: * A closure parameter to `catch_traps` was added. This closure is responsible for classifying addresses as whether or not they fall in JIT code. Anything returning `false` means that the trap won&#39;t get handled and we&#39;ll forward to the next signal handler. * To avoid passing tons of context all over the place, the start function is now no longer automatically invoked by `InstanceHandle`. This avoids the need for passing all sorts of trap-handling contextual information like the maximum stack size and &#34;is this a jit address&#34; closure. Instead creators of `InstanceHandle` (like wasmtime) are now responsible for invoking the start function. * To avoid excessive use of `transmute` with lifetimes since the traphandler state now has a lifetime the per-instance custom signal handler is now replaced with a per-store custom signal handler. I&#39;m not entirely certain the purpose of the custom signal handler, though, so I&#39;d look for feedback on this part. A new test has been added which ensures that if a host function segfaults we don&#39;t accidentally try to handle it, and instead we correctly report the segfault.
5 years ago
store.set_signal_handler(move |signum, siginfo, _| {
handle_sigsegv(base1, length1, signum, siginfo)
});
}
println!("calling instance2.run");
let result = invoke_export(&mut store, instance2, "run")?;
Redo the statically typed `Func` API (#2719) * Redo the statically typed `Func` API This commit reimplements the `Func` API with respect to statically typed dispatch. Previously `Func` had a `getN` and `getN_async` family of methods which were implemented for 0 to 16 parameters. The return value of these functions was an `impl Fn(..)` closure with the appropriate parameters and return values. There are a number of downsides with this approach that have become apparent over time: * The addition of `*_async` doubled the API surface area (which is quite large here due to one-method-per-number-of-parameters). * The [documentation of `Func`][old-docs] are quite verbose and feel &#34;polluted&#34; with all these getters, making it harder to understand the other methods that can be used to interact with a `Func`. * These methods unconditionally pay the cost of returning an owned `impl Fn` with a `&#39;static` lifetime. While cheap, this is still paying the cost for cloning the `Store` effectively and moving data into the closed-over environment. * Storage of the return value into a struct, for example, always requires `Box`-ing the returned closure since it otherwise cannot be named. * Recently I had the desire to implement an &#34;unchecked&#34; path for invoking wasm where you unsafely assert the type signature of a wasm function. Doing this with today&#39;s scheme would require doubling (again) the API surface area for both async and synchronous calls, further polluting the documentation. The main benefit of the previous scheme is that by returning a `impl Fn` it was quite easy and ergonomic to actually invoke the function. In practice, though, examples would often have something akin to `.get0::&lt;()&gt;()?()?` which is a lot of things to interpret all at once. Note that `get0` means &#34;0 parameters&#34; yet a type parameter is passed. There&#39;s also a double function invocation which looks like a lot of characters all lined up in a row. Overall, I think that the previous design is starting to show too many cracks and deserves a rewrite. This commit is that rewrite. The new design in this commit is to delete the `getN{,_async}` family of functions and instead have a new API: impl Func { fn typed&lt;P, R&gt;(&amp;self) -&gt; Result&lt;&amp;Typed&lt;P, R&gt;&gt;; } impl Typed&lt;P, R&gt; { fn call(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; async fn call_async(&amp;self, params: P) -&gt; Result&lt;R, Trap&gt;; } This should entirely replace the current scheme, albeit by slightly losing ergonomics use cases. The idea behind the API is that the existence of `Typed&lt;P, R&gt;` is a &#34;proof&#34; that the underlying function takes `P` and returns `R`. The `Func::typed` method peforms a runtime type-check to ensure that types all match up, and if successful you get a `Typed` value. Otherwise an error is returned. Once you have a `Typed` then, like `Func`, you can either `call` or `call_async`. The difference with a `Typed`, however, is that the params/results are statically known and hence these calls can be much more efficient. This is a much smaller API surface area from before and should greatly simplify the `Func` documentation. There&#39;s still a problem where `Func::wrapN_async` produces a lot of functions to document, but that&#39;s now the sole offender. It&#39;s a nice benefit that the statically-typed-async verisons are now expressed with an `async` function rather than a function-returning-a-future which makes it both more efficient and easier to understand. The type `P` and `R` are intended to either be bare types (e.g. `i32`) or tuples of any length (including 0). At this time `R` is only allowed to be `()` or a bare `i32`-style type because multi-value is not supported with a native ABI (yet). The `P`, however, can be any size of tuples of parameters. This is also where some ergonomics are lost because instead of `f(1, 2)` you now have to write `f.call((1, 2))` (note the double-parens). Similarly `f()` becomes `f.call(())`. Overall I feel that this is a better tradeoff than before. While not universally better due to the loss in ergonomics I feel that this design is much more flexible in terms of what you can do with the return value and also understanding the API surface area (just less to take in). [old-docs]: https://docs.rs/wasmtime/0.24.0/wasmtime/struct.Func.html#method.get0 * Rename Typed to TypedFunc * Implement multi-value returns through `Func::typed` * Fix examples in docs * Fix some more errors * More test fixes * Rebasing and adding `get_typed_func` * Updating tests * Fix typo * More doc tweaks * Tweak visibility on `Func::invoke` * Fix tests again
4 years ago
assert_eq!(123, result);
Ok(())
}
}