wasmtime: Ensure that Func::wrap'd return values are compatible with the current store
This commit is contained in:
@@ -202,7 +202,18 @@ macro_rules! getters {
|
|||||||
let weak_store = instance.store.weak();
|
let weak_store = instance.store.weak();
|
||||||
let weak_store = WeakStore(&weak_store);
|
let weak_store = WeakStore(&weak_store);
|
||||||
|
|
||||||
$( let $args = $args.into_abi_for_arg(weak_store); )*
|
$(
|
||||||
|
// Because this returned closure is not marked `unsafe`,
|
||||||
|
// we have to check that incoming values are compatible
|
||||||
|
// with our store.
|
||||||
|
if !$args.compatible_with_store(weak_store) {
|
||||||
|
return Err(Trap::new(
|
||||||
|
"attempt to pass cross-`Store` value to Wasm as function argument"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let $args = $args.into_abi_for_arg(weak_store);
|
||||||
|
)*
|
||||||
|
|
||||||
invoke_wasm_and_catch_traps(anyfunc.as_ref().vmctx, &instance.store, || {
|
invoke_wasm_and_catch_traps(anyfunc.as_ref().vmctx, &instance.store, || {
|
||||||
ret = Some(fnptr(
|
ret = Some(fnptr(
|
||||||
@@ -827,6 +838,10 @@ pub unsafe trait WasmTy {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
type Abi: Copy;
|
type Abi: Copy;
|
||||||
|
|
||||||
|
// Is this value compatible with the given store?
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool;
|
||||||
|
|
||||||
// Convert this value into its ABI representation, when passing a value into
|
// Convert this value into its ABI representation, when passing a value into
|
||||||
// Wasm as an argument.
|
// Wasm as an argument.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@@ -871,6 +886,10 @@ pub unsafe trait WasmRet {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
type Abi: Copy;
|
type Abi: Copy;
|
||||||
|
|
||||||
|
// Same as `WasmTy::compatible_with_store`.
|
||||||
|
#[doc(hidden)]
|
||||||
|
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool;
|
||||||
|
|
||||||
// Similar to `WasmTy::into_abi_for_arg` but used when host code is
|
// Similar to `WasmTy::into_abi_for_arg` but used when host code is
|
||||||
// returning a value into Wasm, rather than host code passing an argument to
|
// returning a value into Wasm, rather than host code passing an argument to
|
||||||
// a Wasm call. Unlike `into_abi_for_arg`, implementors of this method can
|
// a Wasm call. Unlike `into_abi_for_arg`, implementors of this method can
|
||||||
@@ -904,6 +923,11 @@ pub unsafe trait WasmRet {
|
|||||||
unsafe impl WasmTy for () {
|
unsafe impl WasmTy for () {
|
||||||
type Abi = Self;
|
type Abi = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {}
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {}
|
||||||
|
|
||||||
@@ -926,6 +950,11 @@ unsafe impl WasmTy for () {
|
|||||||
unsafe impl WasmTy for i32 {
|
unsafe impl WasmTy for i32 {
|
||||||
type Abi = Self;
|
type Abi = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
||||||
self
|
self
|
||||||
@@ -966,6 +995,11 @@ unsafe impl WasmTy for i32 {
|
|||||||
unsafe impl WasmTy for u32 {
|
unsafe impl WasmTy for u32 {
|
||||||
type Abi = <i32 as WasmTy>::Abi;
|
type Abi = <i32 as WasmTy>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
||||||
self as i32
|
self as i32
|
||||||
@@ -998,6 +1032,11 @@ unsafe impl WasmTy for u32 {
|
|||||||
unsafe impl WasmTy for i64 {
|
unsafe impl WasmTy for i64 {
|
||||||
type Abi = Self;
|
type Abi = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
||||||
self
|
self
|
||||||
@@ -1038,6 +1077,11 @@ unsafe impl WasmTy for i64 {
|
|||||||
unsafe impl WasmTy for u64 {
|
unsafe impl WasmTy for u64 {
|
||||||
type Abi = <i64 as WasmTy>::Abi;
|
type Abi = <i64 as WasmTy>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
||||||
self as i64
|
self as i64
|
||||||
@@ -1070,6 +1114,11 @@ unsafe impl WasmTy for u64 {
|
|||||||
unsafe impl WasmTy for f32 {
|
unsafe impl WasmTy for f32 {
|
||||||
type Abi = Self;
|
type Abi = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
||||||
self
|
self
|
||||||
@@ -1110,6 +1159,11 @@ unsafe impl WasmTy for f32 {
|
|||||||
unsafe impl WasmTy for f64 {
|
unsafe impl WasmTy for f64 {
|
||||||
type Abi = Self;
|
type Abi = Self;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
||||||
self
|
self
|
||||||
@@ -1150,6 +1204,11 @@ unsafe impl WasmTy for f64 {
|
|||||||
unsafe impl WasmTy for Option<ExternRef> {
|
unsafe impl WasmTy for Option<ExternRef> {
|
||||||
type Abi = *mut u8;
|
type Abi = *mut u8;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, _store: WeakStore<'a>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, store: WeakStore<'a>) -> Self::Abi {
|
||||||
if let Some(x) = self {
|
if let Some(x) = self {
|
||||||
@@ -1205,6 +1264,16 @@ unsafe impl WasmTy for Option<ExternRef> {
|
|||||||
unsafe impl WasmTy for Option<Func> {
|
unsafe impl WasmTy for Option<Func> {
|
||||||
type Abi = *mut wasmtime_runtime::VMCallerCheckedAnyfunc;
|
type Abi = *mut wasmtime_runtime::VMCallerCheckedAnyfunc;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool {
|
||||||
|
if let Some(f) = self {
|
||||||
|
let store = Store::upgrade(store.0).unwrap();
|
||||||
|
Store::same(&store, f.store())
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
fn into_abi_for_arg<'a>(self, _store: WeakStore<'a>) -> Self::Abi {
|
||||||
if let Some(f) = self {
|
if let Some(f) = self {
|
||||||
@@ -1251,6 +1320,11 @@ where
|
|||||||
{
|
{
|
||||||
type Abi = <T as WasmTy>::Abi;
|
type Abi = <T as WasmTy>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool {
|
||||||
|
<Self as WasmTy>::compatible_with_store(self, store)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi {
|
unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi {
|
||||||
<Self as WasmTy>::into_abi_for_arg(self, store)
|
<Self as WasmTy>::into_abi_for_arg(self, store)
|
||||||
@@ -1288,6 +1362,14 @@ where
|
|||||||
{
|
{
|
||||||
type Abi = <T as WasmTy>::Abi;
|
type Abi = <T as WasmTy>::Abi;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn compatible_with_store<'a>(&self, store: WeakStore<'a>) -> bool {
|
||||||
|
match self {
|
||||||
|
Ok(x) => <T as WasmTy>::compatible_with_store(x, store),
|
||||||
|
Err(_) => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi {
|
unsafe fn into_abi_for_ret<'a>(self, store: WeakStore<'a>) -> Self::Abi {
|
||||||
match self {
|
match self {
|
||||||
@@ -1419,6 +1501,27 @@ impl Caller<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
unsafe fn raise_cross_store_trap() -> ! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CrossStoreError;
|
||||||
|
|
||||||
|
impl std::error::Error for CrossStoreError {}
|
||||||
|
|
||||||
|
impl fmt::Display for CrossStoreError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"host function attempted to return cross-`Store` \
|
||||||
|
value to Wasm",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raise_user_trap(Box::new(CrossStoreError));
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_into_func {
|
macro_rules! impl_into_func {
|
||||||
($(
|
($(
|
||||||
($($args:ident)*)
|
($($args:ident)*)
|
||||||
@@ -1482,8 +1585,17 @@ macro_rules! impl_into_func {
|
|||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
match ret {
|
match ret {
|
||||||
Ok(ret) => ret.into_abi_for_ret(weak_store),
|
|
||||||
Err(panic) => wasmtime_runtime::resume_panic(panic),
|
Err(panic) => wasmtime_runtime::resume_panic(panic),
|
||||||
|
Ok(ret) => {
|
||||||
|
// Because the wrapped function is not `unsafe`, we
|
||||||
|
// can't assume it returned a value that is
|
||||||
|
// compatible with this store.
|
||||||
|
if !ret.compatible_with_store(weak_store) {
|
||||||
|
raise_cross_store_trap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.into_abi_for_ret(weak_store)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -442,3 +442,68 @@ fn func_write_nothing() -> anyhow::Result<()> {
|
|||||||
.contains("function attempted to return an incompatible value"));
|
.contains("function attempted to return an incompatible value"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Note: Cranelift only supports refrerence types (used in the wasm in this
|
||||||
|
// test) on x64.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn return_cross_store_value() -> anyhow::Result<()> {
|
||||||
|
let wasm = wat::parse_str(
|
||||||
|
r#"
|
||||||
|
(import "" "" (func (result funcref)))
|
||||||
|
|
||||||
|
(func (export "run") (result funcref)
|
||||||
|
call 0
|
||||||
|
)
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.wasm_reference_types(true);
|
||||||
|
let engine = Engine::new(&config);
|
||||||
|
let module = Module::new(&engine, &wasm)?;
|
||||||
|
|
||||||
|
let store1 = Store::new(&engine);
|
||||||
|
let store2 = Store::new(&engine);
|
||||||
|
|
||||||
|
let store2_func = Func::wrap(&store2, || {});
|
||||||
|
let return_cross_store_func = Func::wrap(&store1, move || Some(store2_func.clone()));
|
||||||
|
|
||||||
|
let instance = Instance::new(&store1, &module, &[return_cross_store_func.into()])?;
|
||||||
|
|
||||||
|
let run = instance.get_func("run").unwrap();
|
||||||
|
let result = run.call(&[]);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.unwrap_err().to_string().contains("cross-`Store`"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Note: Cranelift only supports refrerence types (used in the wasm in this
|
||||||
|
// test) on x64.
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn pass_cross_store_arg() -> anyhow::Result<()> {
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.wasm_reference_types(true);
|
||||||
|
let engine = Engine::new(&config);
|
||||||
|
|
||||||
|
let store1 = Store::new(&engine);
|
||||||
|
let store2 = Store::new(&engine);
|
||||||
|
|
||||||
|
let store1_func = Func::wrap(&store1, |_: Option<Func>| {});
|
||||||
|
let store2_func = Func::wrap(&store2, || {});
|
||||||
|
|
||||||
|
// Using regular `.call` fails with cross-Store arguments.
|
||||||
|
assert!(store1_func
|
||||||
|
.call(&[Val::FuncRef(Some(store2_func.clone()))])
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
// And using `.get` followed by a function call also fails with cross-Store
|
||||||
|
// arguments.
|
||||||
|
let f = store1_func.get1::<Option<Func>, ()>()?;
|
||||||
|
let result = f(Some(store2_func));
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(result.unwrap_err().to_string().contains("cross-`Store`"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user