fix(runtime-core) Remove undefined behavior with mem::transmute.

In the `wrap` functions, we use `std::mem::transmute(&())` to get the
pointer to the value “around” `wrap` (`Fn` has a method `to_raw` which
declares a `wrap` function, which uses `transmute` to retrieve
`Fn`). This is an undefined behavior. It was working until the
`FuncCtx` is introduced. Since then, the undefined behavior was
causing an error with the Singlepass backend.

This patch stores the pointer to `Fn` in `func_env`, so that the
pointer to the user-defined host function is always predictable.
This commit is contained in:
Ivan Enderlin 2019-11-06 13:58:49 +01:00
parent a4ba429ed0
commit c4c88f8af5
2 changed files with 21 additions and 15 deletions

View File

@ -517,18 +517,18 @@ macro_rules! impl_traits {
let func_env = func_ctx.func_env;
let func: &FN = match func_env {
// The imported function is a closure with a
// captured environment.
// The imported function is a regular
// function, a closure without a captured
// environmet, or a closure with a captured
// environment.
Some(func_env) => unsafe {
let func: NonNull<FN> = func_env.cast();
&*func.as_ptr()
},
// The imported function is a regular function
// or a closure without a captured
// environment.
None => unsafe { mem::transmute_copy(&()) }
// This branch is supposed to be unreachable.
None => unreachable!()
};
// Catch unwind in case of errors.
@ -563,7 +563,7 @@ macro_rules! impl_traits {
// `FN` is a function pointer, or a closure
// _without_ a captured environment.
if mem::size_of::<Self>() == 0 {
None
NonNull::new(&self as *const _ as *mut vm::FuncEnv)
}
// `FN` is a closure _with_ a captured
// environment. Grab it.
@ -613,18 +613,18 @@ macro_rules! impl_traits {
let func_env = func_ctx.func_env;
let func: &FN = match func_env {
// The imported function is a closure with a
// captured environment.
// The imported function is a regular
// function, a closure without a captured
// environmet, or a closure with a captured
// environment.
Some(func_env) => unsafe {
let func: NonNull<FN> = func_env.cast();
&*func.as_ptr()
},
// The imported function is a regular function
// or a closure without a captured
// environment.
None => unsafe { mem::transmute_copy(&()) }
// This branch is supposed to be unreachable.
None => unreachable!()
};
// Catch unwind in case of errors.
@ -656,7 +656,7 @@ macro_rules! impl_traits {
// `FN` is a function pointer, or a closure
// _without_ a captured environment.
if mem::size_of::<Self>() == 0 {
None
NonNull::new(&self as *const _ as *mut vm::FuncEnv)
}
// `FN` is a closure _with_ a captured
// environment. Grab it.

View File

@ -514,12 +514,18 @@ pub struct FuncEnv {
_private: [u8; 0],
}
/// Represents a function context. It is used by imported function
/// Represents a function context. It is used by imported functions
/// only.
#[derive(Debug)]
#[repr(C)]
pub(crate) struct FuncCtx {
/// The `Ctx` pointer.
pub(crate) vmctx: NonNull<Ctx>,
/// A pointer to the function environment. It is used by imported
/// functions only to store the pointer to the real host function,
/// whether it is a regular function, or a closure with or without
/// a captured environment.
pub(crate) func_env: Option<NonNull<FuncEnv>>,
}