runtime: use anyhow::Error instead of Box<dyn std::error::Error...>

This commit is contained in:
Pat Hickey
2021-10-21 11:36:48 -07:00
parent 2225722373
commit a5007f318f
6 changed files with 81 additions and 57 deletions

View File

@@ -12,6 +12,7 @@ use crate::vmcontext::{
VMInterrupts, VMMemoryDefinition, VMMemoryImport, VMTableDefinition, VMTableImport, VMInterrupts, VMMemoryDefinition, VMMemoryImport, VMTableDefinition, VMTableImport,
}; };
use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable, Store}; use crate::{ExportFunction, ExportGlobal, ExportMemory, ExportTable, Store};
use anyhow::Error;
use memoffset::offset_of; use memoffset::offset_of;
use more_asserts::assert_lt; use more_asserts::assert_lt;
use std::alloc::Layout; use std::alloc::Layout;
@@ -348,7 +349,11 @@ impl Instance {
/// Returns `None` if memory can't be grown by the specified amount /// Returns `None` if memory can't be grown by the specified amount
/// of pages. Returns `Some` with the old size in bytes if growth was /// of pages. Returns `Some` with the old size in bytes if growth was
/// successful. /// successful.
pub(crate) fn memory_grow(&mut self, index: MemoryIndex, delta: u64) -> Option<usize> { pub(crate) fn memory_grow(
&mut self,
index: MemoryIndex,
delta: u64,
) -> Result<Option<usize>, Error> {
let (idx, instance) = if let Some(idx) = self.module.defined_memory_index(index) { let (idx, instance) = if let Some(idx) = self.module.defined_memory_index(index) {
(idx, self) (idx, self)
} else { } else {
@@ -387,7 +392,7 @@ impl Instance {
table_index: TableIndex, table_index: TableIndex,
delta: u32, delta: u32,
init_value: TableElement, init_value: TableElement,
) -> Option<u32> { ) -> Result<Option<u32>, Error> {
let (defined_table_index, instance) = let (defined_table_index, instance) =
self.get_defined_table_index_and_instance(table_index); self.get_defined_table_index_and_instance(table_index);
instance.defined_table_grow(defined_table_index, delta, init_value) instance.defined_table_grow(defined_table_index, delta, init_value)
@@ -398,7 +403,7 @@ impl Instance {
table_index: DefinedTableIndex, table_index: DefinedTableIndex,
delta: u32, delta: u32,
init_value: TableElement, init_value: TableElement,
) -> Option<u32> { ) -> Result<Option<u32>, Error> {
let store = unsafe { &mut *self.store() }; let store = unsafe { &mut *self.store() };
let table = self let table = self
.tables .tables

View File

@@ -20,7 +20,7 @@
) )
)] )]
use std::error::Error; use anyhow::Error;
mod export; mod export;
mod externref; mod externref;
@@ -91,15 +91,25 @@ pub unsafe trait Store {
) -> (&mut VMExternRefActivationsTable, &dyn ModuleInfoLookup); ) -> (&mut VMExternRefActivationsTable, &dyn ModuleInfoLookup);
/// Callback invoked to allow the store's resource limiter to reject a memory grow operation. /// Callback invoked to allow the store's resource limiter to reject a memory grow operation.
fn memory_growing(&mut self, current: usize, desired: usize, maximum: Option<usize>) -> bool; fn memory_growing(
&mut self,
current: usize,
desired: usize,
maximum: Option<usize>,
) -> Result<bool, Error>;
/// Callback invoked to notify the store's resource limiter that a memory grow operation has /// Callback invoked to notify the store's resource limiter that a memory grow operation has
/// failed. /// failed.
fn memory_grow_failed(&mut self, error: &anyhow::Error); fn memory_grow_failed(&mut self, error: &Error);
/// Callback invoked to allow the store's resource limiter to reject a table grow operation. /// Callback invoked to allow the store's resource limiter to reject a table grow operation.
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> bool; fn table_growing(
&mut self,
current: u32,
desired: u32,
maximum: Option<u32>,
) -> Result<bool, Error>;
/// Callback invoked whenever fuel runs out by a wasm instance. If an error /// Callback invoked whenever fuel runs out by a wasm instance. If an error
/// is returned that's raised as a trap. Otherwise wasm execution will /// is returned that's raised as a trap. Otherwise wasm execution will
/// continue as normal. /// continue as normal.
fn out_of_gas(&mut self) -> Result<(), Box<dyn Error + Send + Sync>>; fn out_of_gas(&mut self) -> Result<(), Error>;
} }

View File

@@ -193,8 +193,9 @@ pub unsafe extern "C" fn wasmtime_memory32_grow(
let instance = (*vmctx).instance_mut(); let instance = (*vmctx).instance_mut();
let memory_index = MemoryIndex::from_u32(memory_index); let memory_index = MemoryIndex::from_u32(memory_index);
match instance.memory_grow(memory_index, delta) { match instance.memory_grow(memory_index, delta) {
Some(size_in_bytes) => size_in_bytes / (wasmtime_environ::WASM_PAGE_SIZE as usize), Ok(Some(size_in_bytes)) => size_in_bytes / (wasmtime_environ::WASM_PAGE_SIZE as usize),
None => usize::max_value(), Ok(None) => usize::max_value(),
Err(err) => crate::traphandlers::raise_user_trap(err),
} }
} }
@@ -220,9 +221,11 @@ pub unsafe extern "C" fn wasmtime_table_grow(
init_value.into() init_value.into()
} }
}; };
instance match instance.table_grow(table_index, delta, element) {
.table_grow(table_index, delta, element) Ok(Some(r)) => r,
.unwrap_or(-1_i32 as u32) Ok(None) => -1_i32 as u32,
Err(err) => crate::traphandlers::raise_user_trap(err),
}
} }
/// Implementation of `table.fill`. /// Implementation of `table.fill`.
@@ -436,15 +439,6 @@ pub unsafe extern "C" fn wasmtime_externref_global_set(
drop(old); drop(old);
} }
#[derive(Debug)]
struct Unimplemented(&'static str);
impl std::error::Error for Unimplemented {}
impl std::fmt::Display for Unimplemented {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "unimplemented: {}", self.0)
}
}
/// Implementation of `memory.atomic.notify` for locally defined memories. /// Implementation of `memory.atomic.notify` for locally defined memories.
pub unsafe extern "C" fn wasmtime_memory_atomic_notify( pub unsafe extern "C" fn wasmtime_memory_atomic_notify(
vmctx: *mut VMContext, vmctx: *mut VMContext,
@@ -460,9 +454,9 @@ pub unsafe extern "C" fn wasmtime_memory_atomic_notify(
// just to be sure. // just to be sure.
let addr_to_check = addr.checked_add(4).unwrap(); let addr_to_check = addr.checked_add(4).unwrap();
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| { validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
Err(Trap::User(Box::new(Unimplemented( Err(Trap::User(anyhow::anyhow!(
"wasm atomics (fn wasmtime_memory_atomic_notify) unsupported", "unimplemented: wasm atomics (fn wasmtime_memory_atomic_notify) unsupported",
)))) )))
}) })
}; };
match result { match result {
@@ -486,9 +480,9 @@ pub unsafe extern "C" fn wasmtime_memory_atomic_wait32(
// but we still double-check // but we still double-check
let addr_to_check = addr.checked_add(4).unwrap(); let addr_to_check = addr.checked_add(4).unwrap();
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| { validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
Err(Trap::User(Box::new(Unimplemented( Err(Trap::User(anyhow::anyhow!(
"wasm atomics (fn wasmtime_memory_atomic_wait32) unsupported", "unimplemented: wasm atomics (fn wasmtime_memory_atomic_wait32) unsupported",
)))) )))
}) })
}; };
match result { match result {
@@ -512,9 +506,9 @@ pub unsafe extern "C" fn wasmtime_memory_atomic_wait64(
// but we still double-check // but we still double-check
let addr_to_check = addr.checked_add(8).unwrap(); let addr_to_check = addr.checked_add(8).unwrap();
validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| { validate_atomic_addr(instance, memory, addr_to_check).and_then(|()| {
Err(Trap::User(Box::new(Unimplemented( Err(Trap::User(anyhow::anyhow!(
"wasm atomics (fn wasmtime_memory_atomic_wait64) unsupported", "unimplemented: wasm atomics (fn wasmtime_memory_atomic_wait64) unsupported",
)))) )))
}) })
}; };
match result { match result {

View File

@@ -5,6 +5,7 @@
use crate::mmap::Mmap; use crate::mmap::Mmap;
use crate::vmcontext::VMMemoryDefinition; use crate::vmcontext::VMMemoryDefinition;
use crate::Store; use crate::Store;
use anyhow::Error;
use anyhow::{bail, format_err, Result}; use anyhow::{bail, format_err, Result};
use more_asserts::{assert_ge, assert_le}; use more_asserts::{assert_ge, assert_le};
use std::convert::TryFrom; use std::convert::TryFrom;
@@ -315,7 +316,7 @@ impl Memory {
// calculation overflowed. This means that the `minimum` we're informing // calculation overflowed. This means that the `minimum` we're informing
// the limiter is lossy and may not be 100% accurate, but for now the // the limiter is lossy and may not be 100% accurate, but for now the
// expected uses of limiter means that's ok. // expected uses of limiter means that's ok.
if !store.memory_growing(0, minimum.unwrap_or(absolute_max), maximum) { if !store.memory_growing(0, minimum.unwrap_or(absolute_max), maximum)? {
bail!( bail!(
"memory minimum size of {} pages exceeds memory limits", "memory minimum size of {} pages exceeds memory limits",
plan.memory.minimum plan.memory.minimum
@@ -377,11 +378,15 @@ impl Memory {
/// ///
/// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates /// Generally, prefer using `InstanceHandle::memory_grow`, which encapsulates
/// this unsafety. /// this unsafety.
pub unsafe fn grow(&mut self, delta_pages: u64, store: &mut dyn Store) -> Option<usize> { pub unsafe fn grow(
&mut self,
delta_pages: u64,
store: &mut dyn Store,
) -> Result<Option<usize>, Error> {
let old_byte_size = self.byte_size(); let old_byte_size = self.byte_size();
// Wasm spec: when growing by 0 pages, always return the current size. // Wasm spec: when growing by 0 pages, always return the current size.
if delta_pages == 0 { if delta_pages == 0 {
return Some(old_byte_size); return Ok(Some(old_byte_size));
} }
// largest wasm-page-aligned region of memory it is possible to // largest wasm-page-aligned region of memory it is possible to
@@ -402,15 +407,15 @@ impl Memory {
let maximum = self.maximum_byte_size(); let maximum = self.maximum_byte_size();
// Store limiter gets first chance to reject memory_growing. // Store limiter gets first chance to reject memory_growing.
if !store.memory_growing(old_byte_size, new_byte_size, maximum) { if !store.memory_growing(old_byte_size, new_byte_size, maximum)? {
return None; return Ok(None);
} }
// Never exceed maximum, even if limiter permitted it. // Never exceed maximum, even if limiter permitted it.
if let Some(max) = maximum { if let Some(max) = maximum {
if new_byte_size > max { if new_byte_size > max {
store.memory_grow_failed(&format_err!("Memory maximum size exceeded")); store.memory_grow_failed(&format_err!("Memory maximum size exceeded"));
return None; return Ok(None);
} }
} }
@@ -418,7 +423,10 @@ impl Memory {
{ {
if self.is_static() { if self.is_static() {
// Reset any faulted guard pages before growing the memory. // Reset any faulted guard pages before growing the memory.
self.reset_guard_pages().ok()?; if let Err(e) = self.reset_guard_pages() {
store.memory_grow_failed(&e);
return Ok(None);
}
} }
} }
@@ -432,24 +440,27 @@ impl Memory {
// Never exceed static memory size // Never exceed static memory size
if new_byte_size > base.len() { if new_byte_size > base.len() {
store.memory_grow_failed(&format_err!("static memory size exceeded")); store.memory_grow_failed(&format_err!("static memory size exceeded"));
return None; return Ok(None);
} }
// Operating system can fail to make memory accessible // Operating system can fail to make memory accessible
let r = make_accessible( if let Err(e) = make_accessible(
base.as_mut_ptr().add(old_byte_size), base.as_mut_ptr().add(old_byte_size),
new_byte_size - old_byte_size, new_byte_size - old_byte_size,
); ) {
r.map_err(|e| store.memory_grow_failed(&e)).ok()?; store.memory_grow_failed(&e);
return Ok(None);
}
*size = new_byte_size; *size = new_byte_size;
} }
Memory::Dynamic(mem) => { Memory::Dynamic(mem) => {
let r = mem.grow_to(new_byte_size); if let Err(e) = mem.grow_to(new_byte_size) {
r.map_err(|e| store.memory_grow_failed(&e)).ok()?; store.memory_grow_failed(&e);
return Ok(None);
}
} }
} }
Some(old_byte_size) Ok(Some(old_byte_size))
} }
/// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code.

View File

@@ -4,6 +4,7 @@
use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition}; use crate::vmcontext::{VMCallerCheckedAnyfunc, VMTableDefinition};
use crate::{Store, Trap, VMExternRef}; use crate::{Store, Trap, VMExternRef};
use anyhow::Error;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::ops::Range; use std::ops::Range;
@@ -168,7 +169,7 @@ impl Table {
} }
fn limit_new(plan: &TablePlan, store: &mut dyn Store) -> Result<()> { fn limit_new(plan: &TablePlan, store: &mut dyn Store) -> Result<()> {
if !store.table_growing(0, plan.table.minimum, plan.table.maximum) { if !store.table_growing(0, plan.table.minimum, plan.table.maximum)? {
bail!( bail!(
"table minimum size of {} elements exceeds table limits", "table minimum size of {} elements exceeds table limits",
plan.table.minimum plan.table.minimum
@@ -288,17 +289,20 @@ impl Table {
delta: u32, delta: u32,
init_value: TableElement, init_value: TableElement,
store: &mut dyn Store, store: &mut dyn Store,
) -> Option<u32> { ) -> Result<Option<u32>, Error> {
let old_size = self.size(); let old_size = self.size();
let new_size = old_size.checked_add(delta)?; let new_size = match old_size.checked_add(delta) {
Some(s) => s,
None => return Ok(None),
};
if !store.table_growing(old_size, new_size, self.maximum()) { if !store.table_growing(old_size, new_size, self.maximum())? {
return None; return Ok(None);
} }
if let Some(max) = self.maximum() { if let Some(max) = self.maximum() {
if new_size > max { if new_size > max {
return None; return Ok(None);
} }
} }
@@ -320,7 +324,7 @@ impl Table {
self.fill(old_size, init_value, delta) self.fill(old_size, init_value, delta)
.expect("table should not be out of bounds"); .expect("table should not be out of bounds");
Some(old_size) Ok(Some(old_size))
} }
/// Get reference to the specified element. /// Get reference to the specified element.

View File

@@ -2,10 +2,10 @@
//! signalhandling mechanisms. //! signalhandling mechanisms.
use crate::{VMContext, VMInterrupts}; use crate::{VMContext, VMInterrupts};
use anyhow::Error;
use backtrace::Backtrace; use backtrace::Backtrace;
use std::any::Any; use std::any::Any;
use std::cell::{Cell, UnsafeCell}; use std::cell::{Cell, UnsafeCell};
use std::error::Error;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::ptr; use std::ptr;
use std::sync::atomic::Ordering::SeqCst; use std::sync::atomic::Ordering::SeqCst;
@@ -80,7 +80,7 @@ pub fn init_traps(is_wasm_pc: fn(usize) -> bool) {
/// Only safe to call when wasm code is on the stack, aka `catch_traps` must /// Only safe to call when wasm code is on the stack, aka `catch_traps` must
/// have been previously called. Additionally no Rust destructors can be on the /// have been previously called. Additionally no Rust destructors can be on the
/// stack. They will be skipped and not executed. /// 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: Error) -> ! {
tls::with(|info| info.unwrap().unwind_with(UnwindReason::UserTrap(data))) tls::with(|info| info.unwrap().unwind_with(UnwindReason::UserTrap(data)))
} }
@@ -114,7 +114,7 @@ pub unsafe fn resume_panic(payload: Box<dyn Any + Send>) -> ! {
#[derive(Debug)] #[derive(Debug)]
pub enum Trap { 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(Error),
/// A trap raised from jit code /// A trap raised from jit code
Jit { Jit {
@@ -206,7 +206,7 @@ pub struct CallThreadState {
enum UnwindReason { enum UnwindReason {
Panic(Box<dyn Any + Send>), Panic(Box<dyn Any + Send>),
UserTrap(Box<dyn Error + Send + Sync>), UserTrap(Error),
LibTrap(Trap), LibTrap(Trap),
JitTrap { backtrace: Backtrace, pc: usize }, JitTrap { backtrace: Backtrace, pc: usize },
} }