mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-13 05:48:45 +00:00
Improved trap handling
This commit is contained in:
1
lib/api/src/externals/function.rs
vendored
1
lib/api/src/externals/function.rs
vendored
@@ -480,6 +480,7 @@ impl Function {
|
|||||||
// Call the trampoline.
|
// Call the trampoline.
|
||||||
if let Err(error) = unsafe {
|
if let Err(error) = unsafe {
|
||||||
wasmer_call_trampoline(
|
wasmer_call_trampoline(
|
||||||
|
&self.store,
|
||||||
self.exported.vm_function.vmctx,
|
self.exported.vm_function.vmctx,
|
||||||
trampoline,
|
trampoline,
|
||||||
self.exported.vm_function.address,
|
self.exported.vm_function.address,
|
||||||
|
|||||||
@@ -275,7 +275,8 @@ impl Module {
|
|||||||
// of this steps traps, we still need to keep the instance alive
|
// of this steps traps, we still need to keep the instance alive
|
||||||
// as some of the Instance elements may have placed in other
|
// as some of the Instance elements may have placed in other
|
||||||
// instance tables.
|
// instance tables.
|
||||||
self.artifact.finish_instantiation(&instance_handle)?;
|
self.artifact
|
||||||
|
.finish_instantiation(&self.store, &instance_handle)?;
|
||||||
|
|
||||||
Ok(instance_handle)
|
Ok(instance_handle)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ macro_rules! impl_native_traits {
|
|||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
wasmer_vm::wasmer_call_trampoline(
|
wasmer_vm::wasmer_call_trampoline(
|
||||||
|
&self.store,
|
||||||
self.vmctx(),
|
self.vmctx(),
|
||||||
trampoline,
|
trampoline,
|
||||||
self.address(),
|
self.address(),
|
||||||
@@ -145,8 +146,8 @@ macro_rules! impl_native_traits {
|
|||||||
// TODO: we can probably remove this copy by doing some clever `transmute`s.
|
// TODO: we can probably remove this copy by doing some clever `transmute`s.
|
||||||
// we know it's not overlapping because `using_rets_array` is false
|
// we know it's not overlapping because `using_rets_array` is false
|
||||||
std::ptr::copy_nonoverlapping(src_pointer,
|
std::ptr::copy_nonoverlapping(src_pointer,
|
||||||
rets_list,
|
rets_list,
|
||||||
num_rets);
|
num_rets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Rets::from_array(rets_list_array))
|
Ok(Rets::from_array(rets_list_array))
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
use crate::tunables::BaseTunables;
|
use crate::tunables::BaseTunables;
|
||||||
use loupe::MemoryUsage;
|
use loupe::MemoryUsage;
|
||||||
|
use std::any::Any;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
#[cfg(all(feature = "compiler", feature = "engine"))]
|
#[cfg(all(feature = "compiler", feature = "engine"))]
|
||||||
use wasmer_compiler::CompilerConfig;
|
use wasmer_compiler::CompilerConfig;
|
||||||
use wasmer_engine::{Engine, Tunables};
|
use wasmer_engine::{is_wasm_pc, Engine, Tunables};
|
||||||
|
use wasmer_vm::{init_traps, SignalHandler, TrapInfo};
|
||||||
|
|
||||||
/// The store represents all global state that can be manipulated by
|
/// The store represents all global state that can be manipulated by
|
||||||
/// WebAssembly programs. It consists of the runtime representation
|
/// WebAssembly programs. It consists of the runtime representation
|
||||||
@@ -28,10 +30,7 @@ impl Store {
|
|||||||
where
|
where
|
||||||
E: Engine + ?Sized,
|
E: Engine + ?Sized,
|
||||||
{
|
{
|
||||||
Self {
|
Self::new_with_tunables(engine, BaseTunables::for_target(engine.target()))
|
||||||
engine: engine.cloned(),
|
|
||||||
tunables: Arc::new(BaseTunables::for_target(engine.target())),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new `Store` with a specific [`Engine`] and [`Tunables`].
|
/// Creates a new `Store` with a specific [`Engine`] and [`Tunables`].
|
||||||
@@ -39,6 +38,10 @@ impl Store {
|
|||||||
where
|
where
|
||||||
E: Engine + ?Sized,
|
E: Engine + ?Sized,
|
||||||
{
|
{
|
||||||
|
// Make sure the signal handlers are installed.
|
||||||
|
// This is required for handling traps.
|
||||||
|
init_traps(is_wasm_pc);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
engine: engine.cloned(),
|
engine: engine.cloned(),
|
||||||
tunables: Arc::new(tunables),
|
tunables: Arc::new(tunables),
|
||||||
@@ -69,6 +72,19 @@ impl PartialEq for Store {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl TrapInfo for Store {
|
||||||
|
#[inline]
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn custom_signal_handler(&self, call: &dyn Fn(&SignalHandler) -> bool) -> bool {
|
||||||
|
// if let Some(handler) = &*self.inner.signal_handler.borrow() {
|
||||||
|
// return call(handler);
|
||||||
|
// }
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We only implement default if we have assigned a default compiler and engine
|
// We only implement default if we have assigned a default compiler and engine
|
||||||
#[cfg(all(feature = "default-compiler", feature = "default-engine"))]
|
#[cfg(all(feature = "default-compiler", feature = "default-engine"))]
|
||||||
impl Default for Store {
|
impl Default for Store {
|
||||||
|
|||||||
@@ -124,8 +124,9 @@ fn translate_ir_trapcode(trap: ir::TrapCode) -> TrapCode {
|
|||||||
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
|
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
|
||||||
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
|
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
|
||||||
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
|
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
|
||||||
ir::TrapCode::Interrupt => TrapCode::Interrupt,
|
ir::TrapCode::Interrupt => unimplemented!("Interrupts not supported"),
|
||||||
ir::TrapCode::User(_user_code) => unimplemented!("User trap code not supported"),
|
ir::TrapCode::User(_user_code) => unimplemented!("User trap code not supported"),
|
||||||
|
// ir::TrapCode::Interrupt => TrapCode::Interrupt,
|
||||||
// ir::TrapCode::User(user_code) => TrapCode::User(user_code),
|
// ir::TrapCode::User(user_code) => TrapCode::User(user_code),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use wasmer_types::{
|
|||||||
};
|
};
|
||||||
use wasmer_vm::{
|
use wasmer_vm::{
|
||||||
FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, ModuleInfo,
|
FuncDataRegistry, FunctionBodyPtr, InstanceAllocator, InstanceHandle, MemoryStyle, ModuleInfo,
|
||||||
TableStyle, VMSharedSignatureIndex, VMTrampoline,
|
TableStyle, TrapInfo, VMSharedSignatureIndex, VMTrampoline,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An `Artifact` is the product that the `Engine`
|
/// An `Artifact` is the product that the `Engine`
|
||||||
@@ -161,6 +161,7 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
|
|||||||
/// See [`InstanceHandle::finish_instantiation`].
|
/// See [`InstanceHandle::finish_instantiation`].
|
||||||
unsafe fn finish_instantiation(
|
unsafe fn finish_instantiation(
|
||||||
&self,
|
&self,
|
||||||
|
trap_info: &dyn TrapInfo,
|
||||||
handle: &InstanceHandle,
|
handle: &InstanceHandle,
|
||||||
) -> Result<(), InstantiationError> {
|
) -> Result<(), InstantiationError> {
|
||||||
let data_initializers = self
|
let data_initializers = self
|
||||||
@@ -172,7 +173,7 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
handle
|
handle
|
||||||
.finish_instantiation(&data_initializers)
|
.finish_instantiation(trap_info, &data_initializers)
|
||||||
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
|
.map_err(|trap| InstantiationError::Start(RuntimeError::from_trap(trap)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,19 @@ impl RuntimeError {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Trap::OOM { backtrace } => {
|
||||||
|
unimplemented!("OOM memory errors are not yet handled");
|
||||||
|
// match error.downcast::<Self>() {
|
||||||
|
// // The error is already a RuntimeError, we return it directly
|
||||||
|
// Ok(runtime_error) => *runtime_error,
|
||||||
|
// Err(e) => Self::new_with_trace(
|
||||||
|
// &info,
|
||||||
|
// None,
|
||||||
|
// RuntimeErrorSource::User(e),
|
||||||
|
// Backtrace::new_unresolved(),
|
||||||
|
// ),
|
||||||
|
// }
|
||||||
|
}
|
||||||
// A trap caused by an error on the generated machine code for a Wasm function
|
// A trap caused by an error on the generated machine code for a Wasm function
|
||||||
Trap::Wasm {
|
Trap::Wasm {
|
||||||
pc,
|
pc,
|
||||||
@@ -92,7 +105,7 @@ impl RuntimeError {
|
|||||||
Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace)
|
Self::new_with_trace(&info, Some(pc), RuntimeErrorSource::Trap(code), backtrace)
|
||||||
}
|
}
|
||||||
// A trap triggered manually from the Wasmer runtime
|
// A trap triggered manually from the Wasmer runtime
|
||||||
Trap::Runtime {
|
Trap::Lib {
|
||||||
trap_code,
|
trap_code,
|
||||||
backtrace,
|
backtrace,
|
||||||
} => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace),
|
} => Self::new_with_trace(&info, None, RuntimeErrorSource::Trap(trap_code), backtrace),
|
||||||
|
|||||||
@@ -42,6 +42,15 @@ pub struct GlobalFrameInfo {
|
|||||||
ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
|
ranges: BTreeMap<usize, ModuleInfoFrameInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether the `pc`, according to globally registered information,
|
||||||
|
/// is a wasm trap or not.
|
||||||
|
pub fn is_wasm_pc(pc: usize) -> bool {
|
||||||
|
let frame_info = FRAME_INFO.read().unwrap();
|
||||||
|
let module_info = frame_info.module_info(pc);
|
||||||
|
let is_wasm_pc = module_info.is_some();
|
||||||
|
is_wasm_pc
|
||||||
|
}
|
||||||
|
|
||||||
/// An RAII structure used to unregister a module's frame information when the
|
/// An RAII structure used to unregister a module's frame information when the
|
||||||
/// module is destroyed.
|
/// module is destroyed.
|
||||||
#[derive(MemoryUsage)]
|
#[derive(MemoryUsage)]
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ mod error;
|
|||||||
mod frame_info;
|
mod frame_info;
|
||||||
pub use error::RuntimeError;
|
pub use error::RuntimeError;
|
||||||
pub use frame_info::{
|
pub use frame_info::{
|
||||||
register as register_frame_info, FrameInfo, FunctionExtent, GlobalFrameInfoRegistration,
|
is_wasm_pc, register as register_frame_info, FrameInfo, FunctionExtent,
|
||||||
FRAME_INFO,
|
GlobalFrameInfoRegistration, FRAME_INFO,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::global::Global;
|
|||||||
use crate::imports::Imports;
|
use crate::imports::Imports;
|
||||||
use crate::memory::{Memory, MemoryError};
|
use crate::memory::{Memory, MemoryError};
|
||||||
use crate::table::{Table, TableElement};
|
use crate::table::{Table, TableElement};
|
||||||
use crate::trap::{catch_traps, init_traps, Trap, TrapCode};
|
use crate::trap::{catch_traps, init_traps, Trap, TrapCode, TrapInfo};
|
||||||
use crate::vmcontext::{
|
use crate::vmcontext::{
|
||||||
VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody,
|
VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody,
|
||||||
VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport,
|
VMFunctionEnvironment, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport,
|
||||||
@@ -98,10 +98,6 @@ pub(crate) struct Instance {
|
|||||||
/// Hosts can store arbitrary per-instance information here.
|
/// Hosts can store arbitrary per-instance information here.
|
||||||
host_state: Box<dyn Any>,
|
host_state: Box<dyn Any>,
|
||||||
|
|
||||||
/// Handler run when `SIGBUS`, `SIGFPE`, `SIGILL`, or `SIGSEGV` are caught by the instance thread.
|
|
||||||
#[loupe(skip)]
|
|
||||||
pub(crate) signal_handler: Cell<Option<Box<SignalHandler>>>,
|
|
||||||
|
|
||||||
/// Functions to operate on host environments in the imports
|
/// Functions to operate on host environments in the imports
|
||||||
/// and pointers to the environments.
|
/// and pointers to the environments.
|
||||||
///
|
///
|
||||||
@@ -397,7 +393,7 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Invoke the WebAssembly start function of the instance, if one is present.
|
/// Invoke the WebAssembly start function of the instance, if one is present.
|
||||||
fn invoke_start_function(&self) -> Result<(), Trap> {
|
fn invoke_start_function(&self, trap_info: &dyn TrapInfo) -> Result<(), Trap> {
|
||||||
let start_index = match self.module.start_function {
|
let start_index = match self.module.start_function {
|
||||||
Some(idx) => idx,
|
Some(idx) => idx,
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
@@ -426,7 +422,7 @@ impl Instance {
|
|||||||
|
|
||||||
// Make the call.
|
// Make the call.
|
||||||
unsafe {
|
unsafe {
|
||||||
catch_traps(callee_vmctx, || {
|
catch_traps(trap_info, || {
|
||||||
mem::transmute::<*const VMFunctionBody, unsafe extern "C" fn(VMFunctionEnvironment)>(
|
mem::transmute::<*const VMFunctionBody, unsafe extern "C" fn(VMFunctionEnvironment)>(
|
||||||
callee_address,
|
callee_address,
|
||||||
)(callee_vmctx)
|
)(callee_vmctx)
|
||||||
@@ -669,7 +665,7 @@ impl Instance {
|
|||||||
.map_or(true, |n| n as usize > elem.len())
|
.map_or(true, |n| n as usize > elem.len())
|
||||||
|| dst.checked_add(len).map_or(true, |m| m > table.size())
|
|| dst.checked_add(len).map_or(true, |m| m > table.size())
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (dst, src) in (dst..dst + len).zip(src..src + len) {
|
for (dst, src) in (dst..dst + len).zip(src..src + len) {
|
||||||
@@ -702,7 +698,7 @@ impl Instance {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |n| n as usize > table_size)
|
.map_or(true, |n| n as usize > table_size)
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in start_index..(start_index + len) {
|
for i in start_index..(start_index + len) {
|
||||||
@@ -821,7 +817,7 @@ impl Instance {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |m| m > memory.current_length)
|
.map_or(true, |m| m > memory.current_length)
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::HeapAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let src_slice = &data[src as usize..(src + len) as usize];
|
let src_slice = &data[src as usize..(src + len) as usize];
|
||||||
@@ -935,7 +931,6 @@ impl InstanceHandle {
|
|||||||
passive_data,
|
passive_data,
|
||||||
host_state,
|
host_state,
|
||||||
funcrefs,
|
funcrefs,
|
||||||
signal_handler: Cell::new(None),
|
|
||||||
imported_function_envs,
|
imported_function_envs,
|
||||||
vmctx: VMContext {},
|
vmctx: VMContext {},
|
||||||
};
|
};
|
||||||
@@ -1000,9 +995,6 @@ impl InstanceHandle {
|
|||||||
VMBuiltinFunctionsArray::initialized(),
|
VMBuiltinFunctionsArray::initialized(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ensure that our signal handlers are ready for action.
|
|
||||||
init_traps();
|
|
||||||
|
|
||||||
// Perform infallible initialization in this constructor, while fallible
|
// Perform infallible initialization in this constructor, while fallible
|
||||||
// initialization is deferred to the `initialize` method.
|
// initialization is deferred to the `initialize` method.
|
||||||
initialize_passive_elements(instance);
|
initialize_passive_elements(instance);
|
||||||
@@ -1023,6 +1015,7 @@ impl InstanceHandle {
|
|||||||
/// Only safe to call immediately after instantiation.
|
/// Only safe to call immediately after instantiation.
|
||||||
pub unsafe fn finish_instantiation(
|
pub unsafe fn finish_instantiation(
|
||||||
&self,
|
&self,
|
||||||
|
trap_info: &dyn TrapInfo,
|
||||||
data_initializers: &[DataInitializer<'_>],
|
data_initializers: &[DataInitializer<'_>],
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
let instance = self.instance().as_ref();
|
let instance = self.instance().as_ref();
|
||||||
@@ -1033,7 +1026,7 @@ impl InstanceHandle {
|
|||||||
|
|
||||||
// The WebAssembly spec specifies that the start function is
|
// The WebAssembly spec specifies that the start function is
|
||||||
// invoked automatically at instantiation time.
|
// invoked automatically at instantiation time.
|
||||||
instance.invoke_start_function()?;
|
instance.invoke_start_function(trap_info)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1270,34 +1263,6 @@ impl InstanceHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(unix)] {
|
|
||||||
pub type SignalHandler = dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool;
|
|
||||||
|
|
||||||
impl InstanceHandle {
|
|
||||||
/// Set a custom signal handler
|
|
||||||
pub fn set_signal_handler<H>(&self, handler: H)
|
|
||||||
where
|
|
||||||
H: 'static + Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool,
|
|
||||||
{
|
|
||||||
self.instance().as_ref().signal_handler.set(Some(Box::new(handler)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if #[cfg(target_os = "windows")] {
|
|
||||||
pub type SignalHandler = dyn Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool;
|
|
||||||
|
|
||||||
impl InstanceHandle {
|
|
||||||
/// Set a custom signal handler
|
|
||||||
pub fn set_signal_handler<H>(&self, handler: H)
|
|
||||||
where
|
|
||||||
H: 'static + Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool,
|
|
||||||
{
|
|
||||||
self.instance().as_ref().signal_handler.set(Some(Box::new(handler)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute the offset for a memory data initializer.
|
/// Compute the offset for a memory data initializer.
|
||||||
fn get_memory_init_start(init: &DataInitializer<'_>, instance: &Instance) -> usize {
|
fn get_memory_init_start(init: &DataInitializer<'_>, instance: &Instance) -> usize {
|
||||||
let mut start = init.location.offset;
|
let mut start = init.location.offset;
|
||||||
@@ -1363,7 +1328,7 @@ fn initialize_tables(instance: &Instance) -> Result<(), Trap> {
|
|||||||
.checked_add(init.elements.len())
|
.checked_add(init.elements.len())
|
||||||
.map_or(true, |end| end > table.size() as usize)
|
.map_or(true, |end| end > table.size() as usize)
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, func_idx) in init.elements.iter().enumerate() {
|
for (i, func_idx) in init.elements.iter().enumerate() {
|
||||||
@@ -1421,7 +1386,7 @@ fn initialize_memories(
|
|||||||
.checked_add(init.data.len())
|
.checked_add(init.data.len())
|
||||||
.map_or(true, |end| end > memory.current_length.try_into().unwrap())
|
.map_or(true, |end| end > memory.current_length.try_into().unwrap())
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::HeapAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ pub unsafe extern "C" fn wasmer_vm_table_get(
|
|||||||
// TODO: type checking, maybe have specialized accessors
|
// TODO: type checking, maybe have specialized accessors
|
||||||
match instance.table_get(table_index, elem_index) {
|
match instance.table_get(table_index, elem_index) {
|
||||||
Some(table_ref) => table_ref.into(),
|
Some(table_ref) => table_ref.into(),
|
||||||
None => raise_lib_trap(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds)),
|
None => raise_lib_trap(Trap::lib(TrapCode::TableAccessOutOfBounds)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,7 +360,7 @@ pub unsafe extern "C" fn wasmer_vm_imported_table_get(
|
|||||||
// TODO: type checking, maybe have specialized accessors
|
// TODO: type checking, maybe have specialized accessors
|
||||||
match instance.imported_table_get(table_index, elem_index) {
|
match instance.imported_table_get(table_index, elem_index) {
|
||||||
Some(table_ref) => table_ref.into(),
|
Some(table_ref) => table_ref.into(),
|
||||||
None => raise_lib_trap(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds)),
|
None => raise_lib_trap(Trap::lib(TrapCode::TableAccessOutOfBounds)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +668,7 @@ pub unsafe extern "C" fn wasmer_vm_data_drop(vmctx: *mut VMContext, data_index:
|
|||||||
/// `wasmer_call_trampoline` must have been previously called.
|
/// `wasmer_call_trampoline` must have been previously called.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn wasmer_vm_raise_trap(trap_code: TrapCode) -> ! {
|
pub unsafe extern "C" fn wasmer_vm_raise_trap(trap_code: TrapCode) -> ! {
|
||||||
let trap = Trap::new_from_runtime(trap_code);
|
let trap = Trap::lib(trap_code);
|
||||||
raise_lib_trap(trap)
|
raise_lib_trap(trap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,11 +83,11 @@ pub trait Table: fmt::Debug + Send + Sync + MemoryUsage {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |n| n > src_table.size())
|
.map_or(true, |n| n > src_table.size())
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
if dst_index.checked_add(len).map_or(true, |m| m > self.size()) {
|
if dst_index.checked_add(len).map_or(true, |m| m > self.size()) {
|
||||||
return Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::TableAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let srcs = src_index..src_index + len;
|
let srcs = src_index..src_index + len;
|
||||||
@@ -411,7 +411,7 @@ impl Table for LinearTable {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
None => Err(Trap::new_from_runtime(TrapCode::TableAccessOutOfBounds)),
|
None => Err(Trap::lib(TrapCode::TableAccessOutOfBounds)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
|
|
||||||
int RegisterSetjmp(
|
int register_setjmp(
|
||||||
void **buf_storage,
|
void **buf_storage,
|
||||||
void (*body)(void*),
|
void (*body)(void*),
|
||||||
void *payload) {
|
void *payload) {
|
||||||
@@ -16,7 +16,7 @@ int RegisterSetjmp(
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unwind(void *JmpBuf) {
|
void unwind(void *JmpBuf) {
|
||||||
jmp_buf *buf = (jmp_buf*) JmpBuf;
|
jmp_buf *buf = (jmp_buf*) JmpBuf;
|
||||||
longjmp(*buf, 1);
|
longjmp(*buf, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ mod traphandlers;
|
|||||||
pub use trapcode::TrapCode;
|
pub use trapcode::TrapCode;
|
||||||
pub use traphandlers::{
|
pub use traphandlers::{
|
||||||
catch_traps, catch_traps_with_result, raise_lib_trap, raise_user_trap, wasmer_call_trampoline,
|
catch_traps, catch_traps_with_result, raise_lib_trap, raise_user_trap, wasmer_call_trampoline,
|
||||||
Trap,
|
SignalHandler, Trap, TrapInfo,
|
||||||
};
|
};
|
||||||
pub use traphandlers::{init_traps, resume_panic};
|
pub use traphandlers::{init_traps, resume_panic};
|
||||||
|
|||||||
@@ -61,17 +61,8 @@ pub enum TrapCode {
|
|||||||
/// Code that was supposed to have been unreachable was reached.
|
/// Code that was supposed to have been unreachable was reached.
|
||||||
UnreachableCodeReached = 10,
|
UnreachableCodeReached = 10,
|
||||||
|
|
||||||
/// Execution has potentially run too long and may be interrupted.
|
|
||||||
/// This trap is resumable.
|
|
||||||
Interrupt = 11,
|
|
||||||
|
|
||||||
/// An atomic memory access was attempted with an unaligned pointer.
|
/// An atomic memory access was attempted with an unaligned pointer.
|
||||||
UnalignedAtomic = 12,
|
UnalignedAtomic = 12,
|
||||||
|
|
||||||
/// A trap indicating that the runtime was unable to allocate sufficient memory.
|
|
||||||
VMOutOfMemory = 13,
|
|
||||||
// /// A user-defined trap code.
|
|
||||||
// User(u16),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrapCode {
|
impl TrapCode {
|
||||||
@@ -89,10 +80,7 @@ impl TrapCode {
|
|||||||
Self::IntegerDivisionByZero => "integer divide by zero",
|
Self::IntegerDivisionByZero => "integer divide by zero",
|
||||||
Self::BadConversionToInteger => "invalid conversion to integer",
|
Self::BadConversionToInteger => "invalid conversion to integer",
|
||||||
Self::UnreachableCodeReached => "unreachable",
|
Self::UnreachableCodeReached => "unreachable",
|
||||||
Self::Interrupt => "interrupt",
|
|
||||||
Self::UnalignedAtomic => "unaligned atomic access",
|
Self::UnalignedAtomic => "unaligned atomic access",
|
||||||
Self::VMOutOfMemory => "out of memory",
|
|
||||||
// Self::User(_) => unreachable!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,10 +99,7 @@ impl Display for TrapCode {
|
|||||||
Self::IntegerDivisionByZero => "int_divz",
|
Self::IntegerDivisionByZero => "int_divz",
|
||||||
Self::BadConversionToInteger => "bad_toint",
|
Self::BadConversionToInteger => "bad_toint",
|
||||||
Self::UnreachableCodeReached => "unreachable",
|
Self::UnreachableCodeReached => "unreachable",
|
||||||
Self::Interrupt => "interrupt",
|
|
||||||
Self::UnalignedAtomic => "unalign_atom",
|
Self::UnalignedAtomic => "unalign_atom",
|
||||||
Self::VMOutOfMemory => "oom",
|
|
||||||
// User(x) => return write!(f, "user{}", x),
|
|
||||||
};
|
};
|
||||||
f.write_str(identifier)
|
f.write_str(identifier)
|
||||||
}
|
}
|
||||||
@@ -124,23 +109,19 @@ impl FromStr for TrapCode {
|
|||||||
type Err = ();
|
type Err = ();
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
use self::TrapCode::*;
|
|
||||||
match s {
|
match s {
|
||||||
"stk_ovf" => Ok(StackOverflow),
|
"stk_ovf" => Ok(TrapCode::StackOverflow),
|
||||||
"heap_get_oob" => Ok(HeapAccessOutOfBounds),
|
"heap_get_oob" => Ok(TrapCode::HeapAccessOutOfBounds),
|
||||||
"heap_misaligned" => Ok(HeapMisaligned),
|
"heap_misaligned" => Ok(TrapCode::HeapMisaligned),
|
||||||
"table_get_oob" => Ok(TableAccessOutOfBounds),
|
"table_get_oob" => Ok(TrapCode::TableAccessOutOfBounds),
|
||||||
"oob" => Ok(OutOfBounds),
|
"oob" => Ok(TrapCode::OutOfBounds),
|
||||||
"icall_null" => Ok(IndirectCallToNull),
|
"icall_null" => Ok(TrapCode::IndirectCallToNull),
|
||||||
"bad_sig" => Ok(BadSignature),
|
"bad_sig" => Ok(TrapCode::BadSignature),
|
||||||
"int_ovf" => Ok(IntegerOverflow),
|
"int_ovf" => Ok(TrapCode::IntegerOverflow),
|
||||||
"int_divz" => Ok(IntegerDivisionByZero),
|
"int_divz" => Ok(TrapCode::IntegerDivisionByZero),
|
||||||
"bad_toint" => Ok(BadConversionToInteger),
|
"bad_toint" => Ok(TrapCode::BadConversionToInteger),
|
||||||
"unreachable" => Ok(UnreachableCodeReached),
|
"unreachable" => Ok(TrapCode::UnreachableCodeReached),
|
||||||
"interrupt" => Ok(Interrupt),
|
"unalign_atom" => Ok(TrapCode::UnalignedAtomic),
|
||||||
"unalign_atom" => Ok(UnalignedAtomic),
|
|
||||||
"oom" => Ok(VMOutOfMemory),
|
|
||||||
// _ if s.starts_with("user") => s[4..].parse().map(User).map_err(|_| ()),
|
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +132,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
// Everything but user-defined codes.
|
// Everything but user-defined codes.
|
||||||
const CODES: [TrapCode; 13] = [
|
const CODES: [TrapCode; 12] = [
|
||||||
TrapCode::StackOverflow,
|
TrapCode::StackOverflow,
|
||||||
TrapCode::HeapAccessOutOfBounds,
|
TrapCode::HeapAccessOutOfBounds,
|
||||||
TrapCode::HeapMisaligned,
|
TrapCode::HeapMisaligned,
|
||||||
@@ -163,7 +144,6 @@ mod tests {
|
|||||||
TrapCode::IntegerDivisionByZero,
|
TrapCode::IntegerDivisionByZero,
|
||||||
TrapCode::BadConversionToInteger,
|
TrapCode::BadConversionToInteger,
|
||||||
TrapCode::UnreachableCodeReached,
|
TrapCode::UnreachableCodeReached,
|
||||||
TrapCode::Interrupt,
|
|
||||||
TrapCode::UnalignedAtomic,
|
TrapCode::UnalignedAtomic,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -5,24 +5,33 @@
|
|||||||
//! signalhandling mechanisms.
|
//! signalhandling mechanisms.
|
||||||
|
|
||||||
use super::trapcode::TrapCode;
|
use super::trapcode::TrapCode;
|
||||||
use crate::instance::{Instance, SignalHandler};
|
|
||||||
use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMTrampoline};
|
use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMTrampoline};
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::Cell;
|
use std::cell::{Cell, UnsafeCell};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(unix)] {
|
||||||
|
/// Function which may handle custom signals while processing traps.
|
||||||
|
pub type SignalHandler = dyn Fn(libc::c_int, *const libc::siginfo_t, *const libc::c_void) -> bool;
|
||||||
|
} else if #[cfg(target_os = "windows")] {
|
||||||
|
/// Function which may handle custom signals while processing traps.
|
||||||
|
pub type SignalHandler = dyn Fn(winapi::um::winnt::PEXCEPTION_POINTERS) -> bool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn RegisterSetjmp(
|
fn register_setjmp(
|
||||||
jmp_buf: *mut *const u8,
|
jmp_buf: *mut *const u8,
|
||||||
callback: extern "C" fn(*mut u8),
|
callback: extern "C" fn(*mut u8),
|
||||||
payload: *mut u8,
|
payload: *mut u8,
|
||||||
) -> i32;
|
) -> i32;
|
||||||
fn Unwind(jmp_buf: *const u8) -> !;
|
fn unwind(jmp_buf: *const u8) -> !;
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
@@ -160,7 +169,7 @@ cfg_if::cfg_if! {
|
|||||||
} else if jmp_buf as usize == 1 {
|
} else if jmp_buf as usize == 1 {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
Unwind(jmp_buf)
|
unwind(jmp_buf)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -338,44 +347,52 @@ cfg_if::cfg_if! {
|
|||||||
} else if jmp_buf as usize == 1 {
|
} else if jmp_buf as usize == 1 {
|
||||||
EXCEPTION_CONTINUE_EXECUTION
|
EXCEPTION_CONTINUE_EXECUTION
|
||||||
} else {
|
} else {
|
||||||
Unwind(jmp_buf)
|
unwind(jmp_buf)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function performs the low-overhead signal handler initialization that
|
/// Globally-set callback to determine whether a program counter is actually a
|
||||||
/// we want to do eagerly to ensure a more-deterministic global process state.
|
/// wasm trap.
|
||||||
///
|
///
|
||||||
/// This is especially relevant for signal handlers since handler ordering
|
/// This is initialized during `init_traps` below. The definition lives within
|
||||||
/// depends on installation order: the wasm signal handler must run *before*
|
/// `wasmer` currently.
|
||||||
/// the other crash handlers and since POSIX signal handlers work LIFO, this
|
static mut IS_WASM_PC: fn(usize) -> bool = |_| false;
|
||||||
/// function needs to be called at the end of the startup process, after other
|
|
||||||
/// handlers have been installed. This function can thus be called multiple
|
|
||||||
/// times, having no effect after the first call.
|
|
||||||
pub fn init_traps() {
|
|
||||||
static INIT: Once = Once::new();
|
|
||||||
INIT.call_once(real_init);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn real_init() {
|
/// This function is required to be called before any WebAssembly is entered.
|
||||||
unsafe {
|
/// This will configure global state such as signal handlers to prepare the
|
||||||
|
/// process to receive wasm traps.
|
||||||
|
///
|
||||||
|
/// This function must not only be called globally once before entering
|
||||||
|
/// WebAssembly but it must also be called once-per-thread that enters
|
||||||
|
/// WebAssembly. Currently in wasmer's integration this function is called on
|
||||||
|
/// creation of a `Store`.
|
||||||
|
///
|
||||||
|
/// The `is_wasm_pc` argument is used when a trap happens to determine if a
|
||||||
|
/// program counter is the pc of an actual wasm trap or not. This is then used
|
||||||
|
/// to disambiguate faults that happen due to wasm and faults that happen due to
|
||||||
|
/// bugs in Rust or elsewhere.
|
||||||
|
pub fn init_traps(is_wasm_pc: fn(usize) -> bool) {
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
INIT.call_once(|| unsafe {
|
||||||
|
IS_WASM_PC = is_wasm_pc;
|
||||||
platform_init();
|
platform_init();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raises a user-defined trap immediately.
|
/// Raises a user-defined trap immediately.
|
||||||
///
|
///
|
||||||
/// This function performs as-if a wasm trap was just executed, only the trap
|
/// This function performs as-if a wasm trap was just executed, only the trap
|
||||||
/// has a dynamic payload associated with it which is user-provided. This trap
|
/// has a dynamic payload associated with it which is user-provided. This trap
|
||||||
/// payload is then returned from `wasmer_call` and `wasmer_call_trampoline`
|
/// payload is then returned from `catch_traps` below.
|
||||||
/// below.
|
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Only safe to call when wasm code is on the stack, aka `wasmer_call` or
|
/// Only safe to call when wasm code is on the stack, aka `catch_traps` must
|
||||||
/// `wasmer_call_trampoline` must have been previously called.
|
/// have been previously called. Additionally no Rust destructors can be on the
|
||||||
|
/// stack. They will be skipped and not executed.
|
||||||
pub unsafe fn raise_user_trap(data: Box<dyn Error + Send + Sync>) -> ! {
|
pub unsafe fn raise_user_trap(data: Box<dyn Error + Send + Sync>) -> ! {
|
||||||
tls::with(|info| info.unwrap().unwind_with(UnwindReason::UserTrap(data)))
|
tls::with(|info| info.unwrap().unwind_with(UnwindReason::UserTrap(data)))
|
||||||
}
|
}
|
||||||
@@ -383,13 +400,13 @@ pub unsafe fn raise_user_trap(data: Box<dyn Error + Send + Sync>) -> ! {
|
|||||||
/// Raises a trap from inside library code immediately.
|
/// Raises a trap from inside library code immediately.
|
||||||
///
|
///
|
||||||
/// This function performs as-if a wasm trap was just executed. This trap
|
/// This function performs as-if a wasm trap was just executed. This trap
|
||||||
/// payload is then returned from `wasmer_call` and `wasmer_call_trampoline`
|
/// payload is then returned from `catch_traps` below.
|
||||||
/// below.
|
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Only safe to call when wasm code is on the stack, aka `wasmer_call` or
|
/// Only safe to call when wasm code is on the stack, aka `catch_traps` must
|
||||||
/// `wasmer_call_trampoline` must have been previously called.
|
/// have been previously called. Additionally no Rust destructors can be on the
|
||||||
|
/// stack. They will be skipped and not executed.
|
||||||
pub unsafe fn raise_lib_trap(trap: Trap) -> ! {
|
pub unsafe fn raise_lib_trap(trap: Trap) -> ! {
|
||||||
tls::with(|info| info.unwrap().unwind_with(UnwindReason::LibTrap(trap)))
|
tls::with(|info| info.unwrap().unwind_with(UnwindReason::LibTrap(trap)))
|
||||||
}
|
}
|
||||||
@@ -399,8 +416,9 @@ pub unsafe fn raise_lib_trap(trap: Trap) -> ! {
|
|||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Only safe to call when wasm code is on the stack, aka `wasmer_call` or
|
/// Only safe to call when wasm code is on the stack, aka `catch_traps` must
|
||||||
/// `wasmer_call_trampoline` must have been previously called.
|
/// have been previously called. Additionally no Rust destructors can be on the
|
||||||
|
/// stack. They will be skipped and not executed.
|
||||||
pub unsafe fn resume_panic(payload: Box<dyn Any + Send>) -> ! {
|
pub unsafe fn resume_panic(payload: Box<dyn Any + Send>) -> ! {
|
||||||
tls::with(|info| info.unwrap().unwind_with(UnwindReason::Panic(payload)))
|
tls::with(|info| info.unwrap().unwind_with(UnwindReason::Panic(payload)))
|
||||||
}
|
}
|
||||||
@@ -427,9 +445,11 @@ pub enum Trap {
|
|||||||
/// A user-raised trap through `raise_user_trap`.
|
/// A user-raised trap through `raise_user_trap`.
|
||||||
User(Box<dyn Error + Send + Sync>),
|
User(Box<dyn Error + Send + Sync>),
|
||||||
|
|
||||||
/// A trap raised from machine code generated from Wasm
|
/// A trap raised from the Wasm generated code
|
||||||
|
///
|
||||||
|
/// Note: this trap is deterministic (assuming a deterministic host implementation)
|
||||||
Wasm {
|
Wasm {
|
||||||
/// The program counter in generated code where this trap happened.
|
/// The program counter in JIT code where this trap happened.
|
||||||
pc: usize,
|
pc: usize,
|
||||||
/// Native stack backtrace at the time the trap occurred
|
/// Native stack backtrace at the time the trap occurred
|
||||||
backtrace: Backtrace,
|
backtrace: Backtrace,
|
||||||
@@ -437,45 +457,54 @@ pub enum Trap {
|
|||||||
signal_trap: Option<TrapCode>,
|
signal_trap: Option<TrapCode>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// A trap raised manually from the Wasmer VM
|
/// A trap raised from a wasm libcall
|
||||||
Runtime {
|
///
|
||||||
|
/// Note: this trap is deterministic (assuming a deterministic host implementation)
|
||||||
|
Lib {
|
||||||
/// Code of the trap.
|
/// Code of the trap.
|
||||||
trap_code: TrapCode,
|
trap_code: TrapCode,
|
||||||
/// Native stack backtrace at the time the trap occurred
|
/// Native stack backtrace at the time the trap occurred
|
||||||
backtrace: Backtrace,
|
backtrace: Backtrace,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// A trap indicating that the runtime was unable to allocate sufficient memory.
|
||||||
|
///
|
||||||
|
/// Note: this trap is undeterministic, since it depends on the host system.
|
||||||
|
OOM {
|
||||||
|
/// Native stack backtrace at the time the OOM occurred
|
||||||
|
backtrace: Backtrace,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Trap {
|
impl Trap {
|
||||||
/// Construct a new VM `Trap` with the given the program counter, backtrace and an optional
|
/// Construct a new Wasm trap with the given source location and backtrace.
|
||||||
/// trap code associated with the signal received from the kernel.
|
///
|
||||||
/// Wasm traps are Traps that are triggered by the chip when running generated
|
/// Internally saves a backtrace when constructed.
|
||||||
/// code for a Wasm function.
|
pub fn wasm(pc: usize, backtrace: Backtrace, signal_trap: Option<TrapCode>) -> Self {
|
||||||
pub fn new_from_wasm(pc: usize, backtrace: Backtrace, signal_trap: Option<TrapCode>) -> Self {
|
Trap::Wasm {
|
||||||
Self::Wasm {
|
|
||||||
pc,
|
pc,
|
||||||
backtrace,
|
backtrace,
|
||||||
signal_trap,
|
signal_trap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new runtime `Trap` with the given trap code.
|
/// Construct a new Wasm trap with the given trap code.
|
||||||
/// Runtime traps are Traps that are triggered manually from the VM.
|
|
||||||
///
|
///
|
||||||
/// Internally saves a backtrace when constructed.
|
/// Internally saves a backtrace when constructed.
|
||||||
pub fn new_from_runtime(trap_code: TrapCode) -> Self {
|
pub fn lib(trap_code: TrapCode) -> Self {
|
||||||
let backtrace = Backtrace::new_unresolved();
|
let backtrace = Backtrace::new_unresolved();
|
||||||
Self::Runtime {
|
Trap::Lib {
|
||||||
trap_code,
|
trap_code,
|
||||||
backtrace,
|
backtrace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a new Out of Memory (OOM) `Trap`.
|
/// Construct a new OOM trap with the given source location and trap code.
|
||||||
///
|
///
|
||||||
/// Internally saves a backtrace when constructed.
|
/// Internally saves a backtrace when constructed.
|
||||||
pub fn new_from_user(error: Box<dyn Error + Send + Sync>) -> Self {
|
pub fn oom() -> Self {
|
||||||
Self::User(error)
|
let backtrace = Backtrace::new_unresolved();
|
||||||
|
Trap::OOM { backtrace }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,34 +524,29 @@ impl Trap {
|
|||||||
/// Wildly unsafe because it calls raw function pointers and reads/writes raw
|
/// Wildly unsafe because it calls raw function pointers and reads/writes raw
|
||||||
/// function pointers.
|
/// function pointers.
|
||||||
pub unsafe fn wasmer_call_trampoline(
|
pub unsafe fn wasmer_call_trampoline(
|
||||||
|
trap_info: &impl TrapInfo,
|
||||||
vmctx: VMFunctionEnvironment,
|
vmctx: VMFunctionEnvironment,
|
||||||
trampoline: VMTrampoline,
|
trampoline: VMTrampoline,
|
||||||
callee: *const VMFunctionBody,
|
callee: *const VMFunctionBody,
|
||||||
values_vec: *mut u8,
|
values_vec: *mut u8,
|
||||||
) -> Result<(), Trap> {
|
) -> Result<(), Trap> {
|
||||||
catch_traps(vmctx, || {
|
catch_traps(trap_info, || {
|
||||||
mem::transmute::<_, extern "C" fn(VMFunctionEnvironment, *const VMFunctionBody, *mut u8)>(
|
mem::transmute::<_, extern "C" fn(VMFunctionEnvironment, *const VMFunctionBody, *mut u8)>(
|
||||||
trampoline,
|
trampoline,
|
||||||
)(vmctx, callee, values_vec)
|
)(vmctx, callee, values_vec);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Catches any wasm traps that happen within the execution of `closure`,
|
/// Catches any wasm traps that happen within the execution of `closure`,
|
||||||
/// returning them as a `Result`.
|
/// returning them as a `Result`.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// Highly unsafe since `closure` won't have any dtors run.
|
||||||
///
|
pub unsafe fn catch_traps<F>(trap_info: &dyn TrapInfo, mut closure: F) -> Result<(), Trap>
|
||||||
/// Highly unsafe since `closure` won't have any destructors run.
|
|
||||||
pub unsafe fn catch_traps<F>(vmctx: VMFunctionEnvironment, mut closure: F) -> Result<(), Trap>
|
|
||||||
where
|
where
|
||||||
F: FnMut(),
|
F: FnMut(),
|
||||||
{
|
{
|
||||||
// Ensure that we have our sigaltstack installed.
|
return CallThreadState::new(trap_info).with(|cx| {
|
||||||
#[cfg(unix)]
|
register_setjmp(
|
||||||
setup_unix_sigaltstack()?;
|
|
||||||
|
|
||||||
return CallThreadState::new(vmctx).with(|cx| {
|
|
||||||
RegisterSetjmp(
|
|
||||||
cx.jmp_buf.as_ptr(),
|
cx.jmp_buf.as_ptr(),
|
||||||
call_closure::<F>,
|
call_closure::<F>,
|
||||||
&mut closure as *mut F as *mut u8,
|
&mut closure as *mut F as *mut u8,
|
||||||
@@ -547,14 +571,14 @@ where
|
|||||||
///
|
///
|
||||||
/// Check [`catch_traps`].
|
/// Check [`catch_traps`].
|
||||||
pub unsafe fn catch_traps_with_result<F, R>(
|
pub unsafe fn catch_traps_with_result<F, R>(
|
||||||
vmctx: VMFunctionEnvironment,
|
trap_info: &dyn TrapInfo,
|
||||||
mut closure: F,
|
mut closure: F,
|
||||||
) -> Result<R, Trap>
|
) -> Result<R, Trap>
|
||||||
where
|
where
|
||||||
F: FnMut() -> R,
|
F: FnMut() -> R,
|
||||||
{
|
{
|
||||||
let mut global_results = mem::MaybeUninit::<R>::uninit();
|
let mut global_results = MaybeUninit::<R>::uninit();
|
||||||
catch_traps(vmctx, || {
|
catch_traps(trap_info, || {
|
||||||
global_results.as_mut_ptr().write(closure());
|
global_results.as_mut_ptr().write(closure());
|
||||||
})?;
|
})?;
|
||||||
Ok(global_results.assume_init())
|
Ok(global_results.assume_init())
|
||||||
@@ -562,91 +586,89 @@ where
|
|||||||
|
|
||||||
/// Temporary state stored on the stack which is registered in the `tls` module
|
/// Temporary state stored on the stack which is registered in the `tls` module
|
||||||
/// below for calls into wasm.
|
/// below for calls into wasm.
|
||||||
pub struct CallThreadState {
|
pub struct CallThreadState<'a> {
|
||||||
unwind: Cell<UnwindReason>,
|
unwind: UnsafeCell<MaybeUninit<UnwindReason>>,
|
||||||
jmp_buf: Cell<*const u8>,
|
jmp_buf: Cell<*const u8>,
|
||||||
reset_guard_page: Cell<bool>,
|
reset_guard_page: Cell<bool>,
|
||||||
prev: Option<*const CallThreadState>,
|
prev: Cell<tls::Ptr>,
|
||||||
vmctx: VMFunctionEnvironment,
|
trap_info: &'a (dyn TrapInfo + 'a),
|
||||||
handling_trap: Cell<bool>,
|
handling_trap: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A package of functionality needed by `catch_traps` to figure out what to do
|
||||||
|
/// when handling a trap.
|
||||||
|
///
|
||||||
|
/// Note that this is an `unsafe` trait at least because it's being run in the
|
||||||
|
/// context of a synchronous signal handler, so it needs to be careful to not
|
||||||
|
/// access too much state in answering these queries.
|
||||||
|
pub unsafe trait TrapInfo {
|
||||||
|
/// Converts this object into an `Any` to dynamically check its type.
|
||||||
|
fn as_any(&self) -> &dyn Any;
|
||||||
|
|
||||||
|
/// Uses `call` to call a custom signal handler, if one is specified.
|
||||||
|
///
|
||||||
|
/// Returns `true` if `call` returns true, otherwise returns `false`.
|
||||||
|
fn custom_signal_handler(&self, call: &dyn Fn(&SignalHandler) -> bool) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
enum UnwindReason {
|
enum UnwindReason {
|
||||||
None,
|
/// A panic caused by the host
|
||||||
Panic(Box<dyn Any + Send>),
|
Panic(Box<dyn Any + Send>),
|
||||||
|
/// A custom error triggered by the user
|
||||||
UserTrap(Box<dyn Error + Send + Sync>),
|
UserTrap(Box<dyn Error + Send + Sync>),
|
||||||
|
/// A Trap triggered by a wasm libcall
|
||||||
LibTrap(Trap),
|
LibTrap(Trap),
|
||||||
RuntimeTrap {
|
/// A trap caused by the Wasm generated code
|
||||||
|
WasmTrap {
|
||||||
backtrace: Backtrace,
|
backtrace: Backtrace,
|
||||||
pc: usize,
|
pc: usize,
|
||||||
signal_trap: Option<TrapCode>,
|
signal_trap: Option<TrapCode>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallThreadState {
|
impl<'a> CallThreadState<'a> {
|
||||||
fn new(vmctx: VMFunctionEnvironment) -> Self {
|
#[inline]
|
||||||
|
fn new(trap_info: &'a (dyn TrapInfo + 'a)) -> CallThreadState<'a> {
|
||||||
Self {
|
Self {
|
||||||
unwind: Cell::new(UnwindReason::None),
|
unwind: UnsafeCell::new(MaybeUninit::uninit()),
|
||||||
vmctx,
|
|
||||||
jmp_buf: Cell::new(ptr::null()),
|
jmp_buf: Cell::new(ptr::null()),
|
||||||
reset_guard_page: Cell::new(false),
|
reset_guard_page: Cell::new(false),
|
||||||
prev: None,
|
prev: Cell::new(ptr::null()),
|
||||||
|
trap_info,
|
||||||
handling_trap: Cell::new(false),
|
handling_trap: Cell::new(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with(mut self, closure: impl FnOnce(&Self) -> i32) -> Result<(), Trap> {
|
fn with(mut self, closure: impl FnOnce(&CallThreadState) -> i32) -> Result<(), Trap> {
|
||||||
tls::with(|prev| {
|
let ret = tls::set(&self, || closure(&self))?;
|
||||||
self.prev = prev.map(|p| p as *const _);
|
if ret != 0 {
|
||||||
let ret = tls::set(&self, || closure(&self));
|
return Ok(());
|
||||||
match self.unwind.replace(UnwindReason::None) {
|
}
|
||||||
UnwindReason::None => {
|
match unsafe { (*self.unwind.get()).as_ptr().read() } {
|
||||||
debug_assert_eq!(ret, 1);
|
UnwindReason::UserTrap(data) => {
|
||||||
Ok(())
|
debug_assert_eq!(ret, 0);
|
||||||
}
|
Err(Trap::User(data))
|
||||||
UnwindReason::UserTrap(data) => {
|
|
||||||
debug_assert_eq!(ret, 0);
|
|
||||||
Err(Trap::new_from_user(data))
|
|
||||||
}
|
|
||||||
UnwindReason::LibTrap(trap) => Err(trap),
|
|
||||||
UnwindReason::RuntimeTrap {
|
|
||||||
backtrace,
|
|
||||||
pc,
|
|
||||||
signal_trap,
|
|
||||||
} => {
|
|
||||||
debug_assert_eq!(ret, 0);
|
|
||||||
Err(Trap::new_from_wasm(pc, backtrace, signal_trap))
|
|
||||||
}
|
|
||||||
UnwindReason::Panic(panic) => {
|
|
||||||
debug_assert_eq!(ret, 0);
|
|
||||||
std::panic::resume_unwind(panic)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
UnwindReason::LibTrap(trap) => Err(trap),
|
||||||
}
|
UnwindReason::WasmTrap {
|
||||||
|
backtrace,
|
||||||
fn any_instance(&self, func: impl Fn(&Instance) -> bool) -> bool {
|
pc,
|
||||||
unsafe {
|
signal_trap,
|
||||||
if func(
|
} => {
|
||||||
self.vmctx
|
debug_assert_eq!(ret, 0);
|
||||||
.vmctx
|
Err(Trap::wasm(pc, backtrace, signal_trap))
|
||||||
.as_ref()
|
|
||||||
.expect("`VMContext` is null in `any_instance`")
|
|
||||||
.instance(),
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
match self.prev {
|
UnwindReason::Panic(panic) => {
|
||||||
Some(prev) => (*prev).any_instance(func),
|
debug_assert_eq!(ret, 0);
|
||||||
None => false,
|
std::panic::resume_unwind(panic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwind_with(&self, reason: UnwindReason) -> ! {
|
fn unwind_with(&self, reason: UnwindReason) -> ! {
|
||||||
self.unwind.replace(reason);
|
|
||||||
unsafe {
|
unsafe {
|
||||||
Unwind(self.jmp_buf.get());
|
(*self.unwind.get()).as_mut_ptr().write(reason);
|
||||||
|
unwind(self.jmp_buf.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,21 +705,10 @@ impl CallThreadState {
|
|||||||
return ptr::null();
|
return ptr::null();
|
||||||
}
|
}
|
||||||
|
|
||||||
// First up see if any instance registered has a custom trap handler,
|
// First up see if we have a custom trap handler,
|
||||||
// in which case run them all. If anything handles the trap then we
|
// in which case run it. If anything handles the trap then we
|
||||||
// return that the trap was handled.
|
// return that the trap was handled.
|
||||||
let any_instance = self.any_instance(|instance: &Instance| {
|
if self.trap_info.custom_signal_handler(&call_handler) {
|
||||||
let handler = match instance.signal_handler.replace(None) {
|
|
||||||
Some(handler) => handler,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
let result = call_handler(&handler);
|
|
||||||
instance.signal_handler.set(Some(handler));
|
|
||||||
result
|
|
||||||
});
|
|
||||||
|
|
||||||
if any_instance {
|
|
||||||
self.handling_trap.set(false);
|
|
||||||
return 1 as *const _;
|
return 1 as *const _;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,17 +725,21 @@ impl CallThreadState {
|
|||||||
}
|
}
|
||||||
let backtrace = Backtrace::new_unresolved();
|
let backtrace = Backtrace::new_unresolved();
|
||||||
self.reset_guard_page.set(reset_guard_page);
|
self.reset_guard_page.set(reset_guard_page);
|
||||||
self.unwind.replace(UnwindReason::RuntimeTrap {
|
unsafe {
|
||||||
backtrace,
|
(*self.unwind.get())
|
||||||
signal_trap,
|
.as_mut_ptr()
|
||||||
pc: pc as usize,
|
.write(UnwindReason::WasmTrap {
|
||||||
});
|
backtrace,
|
||||||
|
signal_trap,
|
||||||
|
pc: pc as usize,
|
||||||
|
});
|
||||||
|
}
|
||||||
self.handling_trap.set(false);
|
self.handling_trap.set(false);
|
||||||
self.jmp_buf.get()
|
self.jmp_buf.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for CallThreadState {
|
impl<'a> Drop for CallThreadState<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.reset_guard_page.get() {
|
if self.reset_guard_page.get() {
|
||||||
reset_guard_page();
|
reset_guard_page();
|
||||||
@@ -739,39 +754,138 @@ impl Drop for CallThreadState {
|
|||||||
// the caller to the trap site.
|
// the caller to the trap site.
|
||||||
mod tls {
|
mod tls {
|
||||||
use super::CallThreadState;
|
use super::CallThreadState;
|
||||||
use std::cell::Cell;
|
use crate::Trap;
|
||||||
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
thread_local!(static PTR: Cell<*const CallThreadState> = Cell::new(ptr::null()));
|
pub use raw::Ptr;
|
||||||
|
|
||||||
|
// An even *more* inner module for dealing with TLS. This actually has the
|
||||||
|
// thread local variable and has functions to access the variable.
|
||||||
|
//
|
||||||
|
// Note that this is specially done to fully encapsulate that the accessors
|
||||||
|
// for tls must not be inlined. Wasmtime's async support employs stack
|
||||||
|
// switching which can resume execution on different OS threads. This means
|
||||||
|
// that borrows of our TLS pointer must never live across accesses because
|
||||||
|
// otherwise the access may be split across two threads and cause unsafety.
|
||||||
|
//
|
||||||
|
// This also means that extra care is taken by the runtime to save/restore
|
||||||
|
// these TLS values when the runtime may have crossed threads.
|
||||||
|
mod raw {
|
||||||
|
use super::CallThreadState;
|
||||||
|
use crate::Trap;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
pub type Ptr = *const CallThreadState<'static>;
|
||||||
|
|
||||||
|
// The first entry here is the `Ptr` which is what's used as part of the
|
||||||
|
// public interface of this module. The second entry is a boolean which
|
||||||
|
// allows the runtime to perform per-thread initialization if necessary
|
||||||
|
// for handling traps (e.g. setting up ports on macOS and sigaltstack on
|
||||||
|
// Unix).
|
||||||
|
thread_local!(static PTR: Cell<(Ptr, bool)> = Cell::new((ptr::null(), false)));
|
||||||
|
|
||||||
|
#[inline(never)] // see module docs for why this is here
|
||||||
|
pub fn replace(val: Ptr) -> Result<Ptr, Trap> {
|
||||||
|
PTR.with(|p| {
|
||||||
|
// When a new value is configured that means that we may be
|
||||||
|
// entering WebAssembly so check to see if this thread has
|
||||||
|
// performed per-thread initialization for traps.
|
||||||
|
let (prev, mut initialized) = p.get();
|
||||||
|
if !initialized {
|
||||||
|
super::super::lazy_per_thread_init()?;
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
p.set((val, initialized));
|
||||||
|
Ok(prev)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)] // see module docs for why this is here
|
||||||
|
pub fn get() -> Ptr {
|
||||||
|
PTR.with(|p| p.get().0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opaque state used to help control TLS state across stack switches for
|
||||||
|
/// async support.
|
||||||
|
pub struct TlsRestore(raw::Ptr);
|
||||||
|
|
||||||
|
impl TlsRestore {
|
||||||
|
/// Takes the TLS state that is currently configured and returns a
|
||||||
|
/// token that is used to replace it later.
|
||||||
|
///
|
||||||
|
/// This is not a safe operation since it's intended to only be used
|
||||||
|
/// with stack switching found with fibers and async wasmer.
|
||||||
|
pub unsafe fn take() -> Result<TlsRestore, Trap> {
|
||||||
|
// Our tls pointer must be set at this time, and it must not be
|
||||||
|
// null. We need to restore the previous pointer since we're
|
||||||
|
// removing ourselves from the call-stack, and in the process we
|
||||||
|
// null out our own previous field for safety in case it's
|
||||||
|
// accidentally used later.
|
||||||
|
let raw = raw::get();
|
||||||
|
assert!(!raw.is_null());
|
||||||
|
let prev = (*raw).prev.replace(ptr::null());
|
||||||
|
raw::replace(prev)?;
|
||||||
|
Ok(TlsRestore(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restores a previous tls state back into this thread's TLS.
|
||||||
|
///
|
||||||
|
/// This is unsafe because it's intended to only be used within the
|
||||||
|
/// context of stack switching within wasmtime.
|
||||||
|
pub unsafe fn replace(self) -> Result<(), super::Trap> {
|
||||||
|
// We need to configure our previous TLS pointer to whatever is in
|
||||||
|
// TLS at this time, and then we set the current state to ourselves.
|
||||||
|
let prev = raw::get();
|
||||||
|
assert!((*self.0).prev.get().is_null());
|
||||||
|
(*self.0).prev.set(prev);
|
||||||
|
raw::replace(self.0)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configures thread local state such that for the duration of the
|
/// Configures thread local state such that for the duration of the
|
||||||
/// execution of `closure` any call to `with` will yield `ptr`, unless this
|
/// execution of `closure` any call to `with` will yield `ptr`, unless this
|
||||||
/// is recursively called again.
|
/// is recursively called again.
|
||||||
pub fn set<R>(ptr: &CallThreadState, closure: impl FnOnce() -> R) -> R {
|
pub fn set<R>(state: &CallThreadState<'_>, closure: impl FnOnce() -> R) -> Result<R, Trap> {
|
||||||
struct Reset<'a, T: Copy>(&'a Cell<T>, T);
|
struct Reset<'a, 'b>(&'a CallThreadState<'b>);
|
||||||
|
|
||||||
impl<T: Copy> Drop for Reset<'_, T> {
|
impl Drop for Reset<'_, '_> {
|
||||||
|
#[inline]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.0.set(self.1);
|
raw::replace(self.0.prev.replace(ptr::null()))
|
||||||
|
.expect("tls should be previously initialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PTR.with(|p| {
|
// Note that this extension of the lifetime to `'static` should be
|
||||||
let _r = Reset(p, p.replace(ptr));
|
// safe because we only ever access it below with an anonymous
|
||||||
closure()
|
// lifetime, meaning `'static` never leaks out of this module.
|
||||||
})
|
let ptr = unsafe {
|
||||||
|
mem::transmute::<*const CallThreadState<'_>, *const CallThreadState<'static>>(state)
|
||||||
|
};
|
||||||
|
let prev = raw::replace(ptr)?;
|
||||||
|
state.prev.set(prev);
|
||||||
|
let _reset = Reset(state);
|
||||||
|
Ok(closure())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the last pointer configured with `set` above. Panics if `set`
|
/// Returns the last pointer configured with `set` above. Panics if `set`
|
||||||
/// has not been previously called.
|
/// has not been previously called.
|
||||||
pub fn with<R>(closure: impl FnOnce(Option<&CallThreadState>) -> R) -> R {
|
pub fn with<R>(closure: impl FnOnce(Option<&CallThreadState<'_>>) -> R) -> R {
|
||||||
PTR.with(|ptr| {
|
let p = raw::get();
|
||||||
let p = ptr.get();
|
unsafe { closure(if p.is_null() { None } else { Some(&*p) }) }
|
||||||
unsafe { closure(if p.is_null() { None } else { Some(&*p) }) }
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn lazy_per_thread_init() -> Result<(), Trap> {
|
||||||
|
// Unused on Windows
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// A module for registering a custom alternate signal stack (sigaltstack).
|
/// A module for registering a custom alternate signal stack (sigaltstack).
|
||||||
///
|
///
|
||||||
/// Rust's libstd installs an alternate stack with size `SIGSTKSZ`, which is not
|
/// Rust's libstd installs an alternate stack with size `SIGSTKSZ`, which is not
|
||||||
@@ -779,7 +893,7 @@ mod tls {
|
|||||||
/// and registering our own alternate stack that is large enough and has a guard
|
/// and registering our own alternate stack that is large enough and has a guard
|
||||||
/// page.
|
/// page.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn setup_unix_sigaltstack() -> Result<(), Trap> {
|
pub fn lazy_per_thread_init() -> Result<(), Trap> {
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ptr::null_mut;
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
@@ -834,7 +948,7 @@ fn setup_unix_sigaltstack() -> Result<(), Trap> {
|
|||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
if ptr == libc::MAP_FAILED {
|
if ptr == libc::MAP_FAILED {
|
||||||
return Err(Trap::new_from_runtime(TrapCode::VMOutOfMemory));
|
return Err(Trap::oom());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the stack with readable/writable memory and then register it
|
// Prepare the stack with readable/writable memory and then register it
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ impl VMMemoryDefinition {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |m| m > self.current_length)
|
.map_or(true, |m| m > self.current_length)
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::HeapAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let dst = usize::try_from(dst).unwrap();
|
let dst = usize::try_from(dst).unwrap();
|
||||||
@@ -414,7 +414,7 @@ impl VMMemoryDefinition {
|
|||||||
.checked_add(len)
|
.checked_add(len)
|
||||||
.map_or(true, |m| m > self.current_length)
|
.map_or(true, |m| m > self.current_length)
|
||||||
{
|
{
|
||||||
return Err(Trap::new_from_runtime(TrapCode::HeapAccessOutOfBounds));
|
return Err(Trap::lib(TrapCode::HeapAccessOutOfBounds));
|
||||||
}
|
}
|
||||||
|
|
||||||
let dst = isize::try_from(dst).unwrap();
|
let dst = isize::try_from(dst).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user