Deduplicate listings of traps in Wasmtime (#5299)

This commit replaces `wasmtime_environ::TrapCode` with `wasmtime::Trap`.
This is possible with past refactorings which slimmed down the `Trap`
definition in the `wasmtime` crate to a simple `enum`. This means that
there's one less place that all the various trap opcodes need to be
listed in Wasmtime.
This commit is contained in:
Alex Crichton
2022-11-18 16:04:38 -06:00
committed by GitHub
parent 9b7c5e316d
commit 7a31c5b07c
14 changed files with 178 additions and 248 deletions

View File

@@ -32,7 +32,7 @@ use std::sync::{Arc, Mutex};
use wasmparser::{FuncValidatorAllocations, FunctionBody}; use wasmparser::{FuncValidatorAllocations, FunctionBody};
use wasmtime_environ::{ use wasmtime_environ::{
AddressMapSection, CacheStore, CompileError, FilePos, FlagValue, FunctionBodyData, FunctionLoc, AddressMapSection, CacheStore, CompileError, FilePos, FlagValue, FunctionBodyData, FunctionLoc,
InstructionAddressMap, ModuleTranslation, ModuleTypes, PtrSize, StackMapInformation, TrapCode, InstructionAddressMap, ModuleTranslation, ModuleTypes, PtrSize, StackMapInformation, Trap,
TrapEncodingBuilder, TrapInformation, Tunables, VMOffsets, WasmFunctionInfo, TrapEncodingBuilder, TrapInformation, Tunables, VMOffsets, WasmFunctionInfo,
}; };
@@ -1003,18 +1003,18 @@ fn mach_trap_to_trap(trap: &MachTrap) -> TrapInformation {
TrapInformation { TrapInformation {
code_offset: offset, code_offset: offset,
trap_code: match code { trap_code: match code {
ir::TrapCode::StackOverflow => TrapCode::StackOverflow, ir::TrapCode::StackOverflow => Trap::StackOverflow,
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapOutOfBounds, ir::TrapCode::HeapOutOfBounds => Trap::MemoryOutOfBounds,
ir::TrapCode::HeapMisaligned => TrapCode::HeapMisaligned, ir::TrapCode::HeapMisaligned => Trap::HeapMisaligned,
ir::TrapCode::TableOutOfBounds => TrapCode::TableOutOfBounds, ir::TrapCode::TableOutOfBounds => Trap::TableOutOfBounds,
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull, ir::TrapCode::IndirectCallToNull => Trap::IndirectCallToNull,
ir::TrapCode::BadSignature => TrapCode::BadSignature, ir::TrapCode::BadSignature => Trap::BadSignature,
ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow, ir::TrapCode::IntegerOverflow => Trap::IntegerOverflow,
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero, ir::TrapCode::IntegerDivisionByZero => Trap::IntegerDivisionByZero,
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger, ir::TrapCode::BadConversionToInteger => Trap::BadConversionToInteger,
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached, ir::TrapCode::UnreachableCodeReached => Trap::UnreachableCodeReached,
ir::TrapCode::Interrupt => TrapCode::Interrupt, ir::TrapCode::Interrupt => Trap::Interrupt,
ir::TrapCode::User(ALWAYS_TRAP_CODE) => TrapCode::AlwaysTrapAdapter, ir::TrapCode::User(ALWAYS_TRAP_CODE) => Trap::AlwaysTrapAdapter,
// these should never be emitted by wasmtime-cranelift // these should never be emitted by wasmtime-cranelift
ir::TrapCode::User(_) => unreachable!(), ir::TrapCode::User(_) => unreachable!(),

View File

@@ -2,6 +2,7 @@ use crate::obj::ELF_WASMTIME_TRAPS;
use object::write::{Object, StandardSegment}; use object::write::{Object, StandardSegment};
use object::{Bytes, LittleEndian, SectionKind, U32Bytes}; use object::{Bytes, LittleEndian, SectionKind, U32Bytes};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt;
use std::ops::Range; use std::ops::Range;
/// A helper structure to build the custom-encoded section of a wasmtime /// A helper structure to build the custom-encoded section of a wasmtime
@@ -26,29 +27,30 @@ pub struct TrapInformation {
pub code_offset: u32, pub code_offset: u32,
/// Code of the trap. /// Code of the trap.
pub trap_code: TrapCode, pub trap_code: Trap,
} }
/// A trap code describing the reason for a trap. // The code can be accessed from the c-api, where the possible values are
/// // translated into enum values defined there:
/// All trap instructions have an explicit trap code. //
// * `wasm_trap_code` in c-api/src/trap.rs, and
// * `wasmtime_trap_code_enum` in c-api/include/wasmtime/trap.h.
//
// These need to be kept in sync.
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[repr(u8)] #[allow(missing_docs)]
pub enum TrapCode { pub enum Trap {
/// The current stack space was exhausted. /// The current stack space was exhausted.
StackOverflow, StackOverflow,
/// A `heap_addr` instruction detected an out-of-bounds error. /// An out-of-bounds memory access.
/// MemoryOutOfBounds,
/// Note that not all out-of-bounds heap accesses are reported this way;
/// some are detected by a segmentation fault on the heap unmapped or
/// offset-guard pages.
HeapOutOfBounds,
/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address. /// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
HeapMisaligned, HeapMisaligned,
/// A `table_addr` instruction detected an out-of-bounds error. /// An out-of-bounds access to a table.
TableOutOfBounds, TableOutOfBounds,
/// Indirect call to a null table entry. /// Indirect call to a null table entry.
@@ -70,15 +72,45 @@ pub enum TrapCode {
UnreachableCodeReached, UnreachableCodeReached,
/// Execution has potentially run too long and may be interrupted. /// Execution has potentially run too long and may be interrupted.
/// This trap is resumable.
Interrupt, Interrupt,
/// Used for the component model when functions are lifted/lowered in a way /// When the `component-model` feature is enabled this trap represents a
/// that generates a function that always traps. /// function that was `canon lift`'d, then `canon lower`'d, then called.
/// This combination of creation of a function in the component model
/// generates a function that always traps and, when called, produces this
/// flavor of trap.
AlwaysTrapAdapter, AlwaysTrapAdapter,
// if adding a variant here be sure to update the `check!` macro below
/// When wasm code is configured to consume fuel and it runs out of fuel
/// then this trap will be raised.
OutOfFuel,
} }
impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Trap::*;
let desc = match self {
StackOverflow => "call stack exhausted",
MemoryOutOfBounds => "out of bounds memory access",
HeapMisaligned => "misaligned memory access",
TableOutOfBounds => "undefined element: out of bounds table access",
IndirectCallToNull => "uninitialized element",
BadSignature => "indirect call type mismatch",
IntegerOverflow => "integer overflow",
IntegerDivisionByZero => "integer divide by zero",
BadConversionToInteger => "invalid conversion to integer",
UnreachableCodeReached => "wasm `unreachable` instruction executed",
Interrupt => "interrupt",
AlwaysTrapAdapter => "degenerate component adapter called",
OutOfFuel => "all fuel consumed by WebAssembly",
};
write!(f, "wasm trap: {desc}")
}
}
impl std::error::Error for Trap {}
impl TrapEncodingBuilder { impl TrapEncodingBuilder {
/// Appends trap information about a function into this section. /// Appends trap information about a function into this section.
/// ///
@@ -136,7 +168,7 @@ impl TrapEncodingBuilder {
/// The `section` provided is expected to have been built by /// The `section` provided is expected to have been built by
/// `TrapEncodingBuilder` above. Additionally the `offset` should be a relative /// `TrapEncodingBuilder` above. Additionally the `offset` should be a relative
/// offset within the text section of the compilation image. /// offset within the text section of the compilation image.
pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option<TrapCode> { pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option<Trap> {
let mut section = Bytes(section); let mut section = Bytes(section);
// NB: this matches the encoding written by `append_to` above. // NB: this matches the encoding written by `append_to` above.
let count = section.read::<U32Bytes<LittleEndian>>().ok()?; let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
@@ -164,16 +196,16 @@ pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option<TrapCode> {
// FIXME: this could use some sort of derive-like thing to avoid having to // FIXME: this could use some sort of derive-like thing to avoid having to
// deduplicate the names here. // deduplicate the names here.
// //
// This simply converts from the `trap`, a `u8`, to the `TrapCode` enum. // This simply converts from the `trap`, a `u8`, to the `Trap` enum.
macro_rules! check { macro_rules! check {
($($name:ident)*) => ($(if trap == TrapCode::$name as u8 { ($($name:ident)*) => ($(if trap == Trap::$name as u8 {
return Some(TrapCode::$name); return Some(Trap::$name);
})*); })*);
} }
check! { check! {
StackOverflow StackOverflow
HeapOutOfBounds MemoryOutOfBounds
HeapMisaligned HeapMisaligned
TableOutOfBounds TableOutOfBounds
IndirectCallToNull IndirectCallToNull
@@ -184,6 +216,7 @@ pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option<TrapCode> {
UnreachableCodeReached UnreachableCodeReached
Interrupt Interrupt
AlwaysTrapAdapter AlwaysTrapAdapter
OutOfFuel
} }
if cfg!(debug_assertions) { if cfg!(debug_assertions) {

View File

@@ -30,7 +30,7 @@ use wasmtime_environ::{
packed_option::ReservedValue, DataIndex, DefinedGlobalIndex, DefinedMemoryIndex, packed_option::ReservedValue, DataIndex, DefinedGlobalIndex, DefinedMemoryIndex,
DefinedTableIndex, ElemIndex, EntityIndex, EntityRef, EntitySet, FuncIndex, GlobalIndex, DefinedTableIndex, ElemIndex, EntityIndex, EntityRef, EntitySet, FuncIndex, GlobalIndex,
GlobalInit, HostPtr, MemoryIndex, Module, PrimaryMap, SignatureIndex, TableIndex, GlobalInit, HostPtr, MemoryIndex, Module, PrimaryMap, SignatureIndex, TableIndex,
TableInitialization, TrapCode, VMOffsets, WasmType, TableInitialization, Trap, VMOffsets, WasmType,
}; };
mod allocator; mod allocator;
@@ -580,7 +580,7 @@ impl Instance {
dst: u32, dst: u32,
src: u32, src: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
// TODO: this `clone()` shouldn't be necessary but is used for now to // TODO: this `clone()` shouldn't be necessary but is used for now to
// inform `rustc` that the lifetime of the elements here are // inform `rustc` that the lifetime of the elements here are
// disconnected from the lifetime of `self`. // disconnected from the lifetime of `self`.
@@ -602,7 +602,7 @@ impl Instance {
dst: u32, dst: u32,
src: u32, src: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-init
let table = unsafe { &mut *self.get_table(table_index) }; let table = unsafe { &mut *self.get_table(table_index) };
@@ -612,7 +612,7 @@ impl Instance {
.and_then(|s| s.get(..usize::try_from(len).unwrap())) .and_then(|s| s.get(..usize::try_from(len).unwrap()))
{ {
Some(elements) => elements, Some(elements) => elements,
None => return Err(TrapCode::TableOutOfBounds), None => return Err(Trap::TableOutOfBounds),
}; };
match table.element_type() { match table.element_type() {
@@ -662,7 +662,7 @@ impl Instance {
src_index: MemoryIndex, src_index: MemoryIndex,
src: u64, src: u64,
len: u64, len: u64,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
// https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy // https://webassembly.github.io/reference-types/core/exec/instructions.html#exec-memory-copy
let src_mem = self.get_memory(src_index); let src_mem = self.get_memory(src_index);
@@ -684,8 +684,8 @@ impl Instance {
Ok(()) Ok(())
} }
fn validate_inbounds(&self, max: usize, ptr: u64, len: u64) -> Result<usize, TrapCode> { fn validate_inbounds(&self, max: usize, ptr: u64, len: u64) -> Result<usize, Trap> {
let oob = || TrapCode::HeapOutOfBounds; let oob = || Trap::MemoryOutOfBounds;
let end = ptr let end = ptr
.checked_add(len) .checked_add(len)
.and_then(|i| usize::try_from(i).ok()) .and_then(|i| usize::try_from(i).ok())
@@ -708,7 +708,7 @@ impl Instance {
dst: u64, dst: u64,
val: u8, val: u8,
len: u64, len: u64,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let memory = self.get_memory(memory_index); let memory = self.get_memory(memory_index);
let dst = self.validate_inbounds(memory.current_length(), dst, len)?; let dst = self.validate_inbounds(memory.current_length(), dst, len)?;
@@ -738,7 +738,7 @@ impl Instance {
dst: u64, dst: u64,
src: u32, src: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let range = match self.module().passive_data_map.get(&data_index).cloned() { let range = match self.module().passive_data_map.get(&data_index).cloned() {
Some(range) if !self.dropped_data.contains(data_index) => range, Some(range) if !self.dropped_data.contains(data_index) => range,
_ => 0..0, _ => 0..0,
@@ -757,7 +757,7 @@ impl Instance {
dst: u64, dst: u64,
src: u32, src: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-memory-init
let memory = self.get_memory(memory_index); let memory = self.get_memory(memory_index);

View File

@@ -13,8 +13,8 @@ use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
use wasmtime_environ::{ use wasmtime_environ::{
DefinedMemoryIndex, DefinedTableIndex, HostPtr, InitMemory, MemoryInitialization, DefinedMemoryIndex, DefinedTableIndex, HostPtr, InitMemory, MemoryInitialization,
MemoryInitializer, Module, PrimaryMap, TableInitialization, TableInitializer, TrapCode, MemoryInitializer, Module, PrimaryMap, TableInitialization, TableInitializer, Trap, VMOffsets,
VMOffsets, WasmType, WASM_PAGE_SIZE, WasmType, WASM_PAGE_SIZE,
}; };
#[cfg(feature = "pooling-allocator")] #[cfg(feature = "pooling-allocator")]
@@ -105,7 +105,7 @@ pub enum InstantiationError {
/// A trap ocurred during instantiation, after linking. /// A trap ocurred during instantiation, after linking.
#[error("Trap occurred during instantiation")] #[error("Trap occurred during instantiation")]
Trap(TrapCode), Trap(Trap),
/// A limit on how many instances are supported has been reached. /// A limit on how many instances are supported has been reached.
#[error("Limit of {0} concurrent instances has been reached")] #[error("Limit of {0} concurrent instances has been reached")]
@@ -386,7 +386,7 @@ fn initialize_memories(instance: &mut Instance, module: &Module) -> Result<(), I
}, },
); );
if !ok { if !ok {
return Err(InstantiationError::Trap(TrapCode::HeapOutOfBounds)); return Err(InstantiationError::Trap(Trap::MemoryOutOfBounds));
} }
Ok(()) Ok(())

View File

@@ -63,7 +63,7 @@ use anyhow::Result;
use std::mem; use std::mem;
use std::ptr::{self, NonNull}; use std::ptr::{self, NonNull};
use wasmtime_environ::{ use wasmtime_environ::{
DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, TrapCode, DataIndex, ElemIndex, FuncIndex, GlobalIndex, MemoryIndex, TableIndex, Trap,
}; };
/// Actually public trampolines which are used by the runtime as the entrypoint /// Actually public trampolines which are used by the runtime as the entrypoint
@@ -228,7 +228,7 @@ unsafe fn table_fill(
// `VMCallerCheckedAnyfunc` until we look at the table's element type. // `VMCallerCheckedAnyfunc` until we look at the table's element type.
val: *mut u8, val: *mut u8,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let instance = (*vmctx).instance_mut(); let instance = (*vmctx).instance_mut();
let table_index = TableIndex::from_u32(table_index); let table_index = TableIndex::from_u32(table_index);
let table = &mut *instance.get_table(table_index); let table = &mut *instance.get_table(table_index);
@@ -259,7 +259,7 @@ unsafe fn table_copy(
dst: u32, dst: u32,
src: u32, src: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let dst_table_index = TableIndex::from_u32(dst_table_index); let dst_table_index = TableIndex::from_u32(dst_table_index);
let src_table_index = TableIndex::from_u32(src_table_index); let src_table_index = TableIndex::from_u32(src_table_index);
let instance = (*vmctx).instance_mut(); let instance = (*vmctx).instance_mut();
@@ -278,7 +278,7 @@ unsafe fn table_init(
dst: u32, dst: u32,
src: u32, src: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let table_index = TableIndex::from_u32(table_index); let table_index = TableIndex::from_u32(table_index);
let elem_index = ElemIndex::from_u32(elem_index); let elem_index = ElemIndex::from_u32(elem_index);
let instance = (*vmctx).instance_mut(); let instance = (*vmctx).instance_mut();
@@ -300,7 +300,7 @@ unsafe fn memory_copy(
src_index: u32, src_index: u32,
src: u64, src: u64,
len: u64, len: u64,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let src_index = MemoryIndex::from_u32(src_index); let src_index = MemoryIndex::from_u32(src_index);
let dst_index = MemoryIndex::from_u32(dst_index); let dst_index = MemoryIndex::from_u32(dst_index);
let instance = (*vmctx).instance_mut(); let instance = (*vmctx).instance_mut();
@@ -314,7 +314,7 @@ unsafe fn memory_fill(
dst: u64, dst: u64,
val: u32, val: u32,
len: u64, len: u64,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let memory_index = MemoryIndex::from_u32(memory_index); let memory_index = MemoryIndex::from_u32(memory_index);
let instance = (*vmctx).instance_mut(); let instance = (*vmctx).instance_mut();
instance.memory_fill(memory_index, dst, val as u8, len) instance.memory_fill(memory_index, dst, val as u8, len)
@@ -328,7 +328,7 @@ unsafe fn memory_init(
dst: u64, dst: u64,
src: u32, src: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
let memory_index = MemoryIndex::from_u32(memory_index); let memory_index = MemoryIndex::from_u32(memory_index);
let data_index = DataIndex::from_u32(data_index); let data_index = DataIndex::from_u32(data_index);
let instance = (*vmctx).instance_mut(); let instance = (*vmctx).instance_mut();
@@ -498,14 +498,14 @@ unsafe fn validate_atomic_addr(
addr: u64, addr: u64,
access_size: u64, access_size: u64,
access_alignment: u64, access_alignment: u64,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
debug_assert!(access_alignment.is_power_of_two()); debug_assert!(access_alignment.is_power_of_two());
ensure!(addr % access_alignment == 0, TrapCode::HeapMisaligned); ensure!(addr % access_alignment == 0, Trap::HeapMisaligned);
let length = u64::try_from(instance.get_memory(memory).current_length()).unwrap(); let length = u64::try_from(instance.get_memory(memory).current_length()).unwrap();
ensure!( ensure!(
addr.saturating_add(access_size) < length, addr.saturating_add(access_size) < length,
TrapCode::HeapOutOfBounds Trap::MemoryOutOfBounds
); );
Ok(()) Ok(())

View File

@@ -8,7 +8,7 @@ use anyhow::{bail, format_err, Error, Result};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::ops::Range; use std::ops::Range;
use std::ptr; use std::ptr;
use wasmtime_environ::{TablePlan, TrapCode, WasmType, FUNCREF_INIT_BIT, FUNCREF_MASK}; use wasmtime_environ::{TablePlan, Trap, WasmType, FUNCREF_INIT_BIT, FUNCREF_MASK};
/// An element going into or coming out of a table. /// An element going into or coming out of a table.
/// ///
@@ -267,7 +267,7 @@ impl Table {
&mut self, &mut self,
dst: u32, dst: u32,
items: impl ExactSizeIterator<Item = *mut VMCallerCheckedAnyfunc>, items: impl ExactSizeIterator<Item = *mut VMCallerCheckedAnyfunc>,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
assert!(self.element_type() == TableElementType::Func); assert!(self.element_type() == TableElementType::Func);
let elements = match self let elements = match self
@@ -276,7 +276,7 @@ impl Table {
.and_then(|s| s.get_mut(..items.len())) .and_then(|s| s.get_mut(..items.len()))
{ {
Some(elements) => elements, Some(elements) => elements,
None => return Err(TrapCode::TableOutOfBounds), None => return Err(Trap::TableOutOfBounds),
}; };
for (item, slot) in items.zip(elements) { for (item, slot) in items.zip(elements) {
@@ -290,14 +290,14 @@ impl Table {
/// Fill `table[dst..dst + len]` with `val`. /// Fill `table[dst..dst + len]` with `val`.
/// ///
/// Returns a trap error on out-of-bounds accesses. /// Returns a trap error on out-of-bounds accesses.
pub fn fill(&mut self, dst: u32, val: TableElement, len: u32) -> Result<(), TrapCode> { pub fn fill(&mut self, dst: u32, val: TableElement, len: u32) -> Result<(), Trap> {
let start = dst as usize; let start = dst as usize;
let end = start let end = start
.checked_add(len as usize) .checked_add(len as usize)
.ok_or_else(|| TrapCode::TableOutOfBounds)?; .ok_or_else(|| Trap::TableOutOfBounds)?;
if end > self.size() as usize { if end > self.size() as usize {
return Err(TrapCode::TableOutOfBounds); return Err(Trap::TableOutOfBounds);
} }
debug_assert!(self.type_matches(&val)); debug_assert!(self.type_matches(&val));
@@ -412,7 +412,7 @@ impl Table {
dst_index: u32, dst_index: u32,
src_index: u32, src_index: u32,
len: u32, len: u32,
) -> Result<(), TrapCode> { ) -> Result<(), Trap> {
// https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy // https://webassembly.github.io/bulk-memory-operations/core/exec/instructions.html#exec-table-copy
if src_index if src_index
@@ -422,7 +422,7 @@ impl Table {
.checked_add(len) .checked_add(len)
.map_or(true, |m| m > (*dst_table).size()) .map_or(true, |m| m > (*dst_table).size())
{ {
return Err(TrapCode::TableOutOfBounds); return Err(Trap::TableOutOfBounds);
} }
debug_assert!( debug_assert!(

View File

@@ -10,7 +10,6 @@ use std::cell::{Cell, UnsafeCell};
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::ptr; use std::ptr;
use std::sync::Once; use std::sync::Once;
use wasmtime_environ::TrapCode;
pub use self::backtrace::Backtrace; pub use self::backtrace::Backtrace;
pub use self::tls::{tls_eager_initialize, TlsRestore}; pub use self::tls::{tls_eager_initialize, TlsRestore};
@@ -112,7 +111,7 @@ pub unsafe fn raise_user_trap(error: Error, needs_backtrace: 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_lib_trap(trap: TrapCode) -> ! { pub unsafe fn raise_lib_trap(trap: wasmtime_environ::Trap) -> ! {
raise_trap(TrapReason::Wasm(trap)) raise_trap(TrapReason::Wasm(trap))
} }
@@ -153,7 +152,7 @@ pub enum TrapReason {
Jit(usize), Jit(usize),
/// A trap raised from a wasm libcall /// A trap raised from a wasm libcall
Wasm(TrapCode), Wasm(wasmtime_environ::Trap),
} }
impl TrapReason { impl TrapReason {
@@ -185,8 +184,8 @@ impl From<Error> for TrapReason {
} }
} }
impl From<TrapCode> for TrapReason { impl From<wasmtime_environ::Trap> for TrapReason {
fn from(code: TrapCode) -> Self { fn from(code: wasmtime_environ::Trap) -> Self {
TrapReason::Wasm(code) TrapReason::Wasm(code)
} }
} }

View File

@@ -1,7 +1,7 @@
use crate::component::func::{Memory, MemoryMut, Options}; use crate::component::func::{Memory, MemoryMut, Options};
use crate::component::storage::slice_to_storage_mut; use crate::component::storage::slice_to_storage_mut;
use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Type, Val}; use crate::component::{ComponentNamedList, ComponentType, Lift, Lower, Type, Val};
use crate::{AsContextMut, StoreContextMut, Trap, ValRaw}; use crate::{AsContextMut, StoreContextMut, ValRaw};
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use std::any::Any; use std::any::Any;
use std::mem::{self, MaybeUninit}; use std::mem::{self, MaybeUninit};
@@ -270,7 +270,7 @@ fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<us
unsafe fn handle_result(func: impl FnOnce() -> Result<()>) { unsafe fn handle_result(func: impl FnOnce() -> Result<()>) {
match panic::catch_unwind(AssertUnwindSafe(func)) { match panic::catch_unwind(AssertUnwindSafe(func)) {
Ok(Ok(())) => {} Ok(Ok(())) => {}
Ok(Err(e)) => Trap::raise(e), Ok(Err(e)) => crate::trap::raise(e),
Err(e) => wasmtime_runtime::resume_panic(e), Err(e) => wasmtime_runtime::resume_panic(e),
} }
} }

View File

@@ -2,7 +2,7 @@ use crate::store::{StoreData, StoreOpaque, Stored};
use crate::trampoline::{generate_global_export, generate_table_export}; use crate::trampoline::{generate_global_export, generate_table_export};
use crate::{ use crate::{
AsContext, AsContextMut, Engine, ExternRef, ExternType, Func, GlobalType, Memory, Mutability, AsContext, AsContextMut, Engine, ExternRef, ExternType, Func, GlobalType, Memory, Mutability,
SharedMemory, TableType, Trap, Val, ValType, SharedMemory, TableType, Val, ValType,
}; };
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use std::mem; use std::mem;
@@ -462,9 +462,7 @@ impl Table {
let init = init.into_table_element(store, ty.element())?; let init = init.into_table_element(store, ty.element())?;
unsafe { unsafe {
let table = Table::from_wasmtime_table(wasmtime_export, store); let table = Table::from_wasmtime_table(wasmtime_export, store);
(*table.wasmtime_table(store, std::iter::empty())) (*table.wasmtime_table(store, std::iter::empty())).fill(0, init, ty.minimum())?;
.fill(0, init, ty.minimum())
.map_err(|c| Trap::from_env(c))?;
Ok(table) Ok(table)
} }
@@ -652,8 +650,7 @@ impl Table {
let src_range = src_index..(src_index.checked_add(len).unwrap_or(u32::MAX)); let src_range = src_index..(src_index.checked_add(len).unwrap_or(u32::MAX));
let src_table = src_table.wasmtime_table(store, src_range); let src_table = src_table.wasmtime_table(store, src_range);
unsafe { unsafe {
runtime::Table::copy(dst_table, src_table, dst_index, src_index, len) runtime::Table::copy(dst_table, src_table, dst_index, src_index, len)?;
.map_err(|c| Trap::from_env(c))?;
} }
Ok(()) Ok(())
} }
@@ -681,9 +678,7 @@ impl Table {
let table = self.wasmtime_table(store, std::iter::empty()); let table = self.wasmtime_table(store, std::iter::empty());
unsafe { unsafe {
(*table) (*table).fill(dst, val, len)?;
.fill(dst, val, len)
.map_err(|c| Trap::from_env(c))?;
} }
Ok(()) Ok(())

View File

@@ -1,7 +1,7 @@
use crate::store::{StoreData, StoreOpaque, Stored}; use crate::store::{StoreData, StoreOpaque, Stored};
use crate::{ use crate::{
AsContext, AsContextMut, CallHook, Engine, Extern, FuncType, Instance, StoreContext, AsContext, AsContextMut, CallHook, Engine, Extern, FuncType, Instance, StoreContext,
StoreContextMut, Trap, Val, ValRaw, ValType, StoreContextMut, Val, ValRaw, ValType,
}; };
use anyhow::{bail, Context as _, Error, Result}; use anyhow::{bail, Context as _, Error, Result};
use std::future::Future; use std::future::Future;
@@ -343,6 +343,8 @@ impl Func {
/// ///
/// For more information about errors in Wasmtime see the [`Trap`] /// For more information about errors in Wasmtime see the [`Trap`]
/// documentation. /// documentation.
///
/// [`Trap`]: crate::Trap
#[cfg(compiler)] #[cfg(compiler)]
#[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs #[cfg_attr(nightlydoc, doc(cfg(feature = "cranelift")))] // see build.rs
pub fn new<T>( pub fn new<T>(
@@ -581,6 +583,8 @@ impl Func {
/// For more information about errors in Wasmtime see the [`Trap`] /// For more information about errors in Wasmtime see the [`Trap`]
/// documentation. /// documentation.
/// ///
/// [`Trap`]: crate::Trap
///
/// # Examples /// # Examples
/// ///
/// First up we can see how simple wasm imports can be implemented, such /// First up we can see how simple wasm imports can be implemented, such
@@ -816,6 +820,8 @@ impl Func {
/// Errors typically indicate that execution of WebAssembly was halted /// Errors typically indicate that execution of WebAssembly was halted
/// mid-way and did not complete after the error condition happened. /// mid-way and did not complete after the error condition happened.
/// ///
/// [`Trap`]: crate::Trap
///
/// # Panics /// # Panics
/// ///
/// This function will panic if called on a function belonging to an async /// This function will panic if called on a function belonging to an async
@@ -1305,7 +1311,7 @@ pub(crate) fn invoke_wasm_and_catch_traps<T>(
); );
exit_wasm(store, exit); exit_wasm(store, exit);
store.0.call_hook(CallHook::ReturningFromWasm)?; store.0.call_hook(CallHook::ReturningFromWasm)?;
result.map_err(|t| Trap::from_runtime_box(store.0, t)) result.map_err(|t| crate::trap::from_runtime_box(store.0, t))
} }
} }
@@ -1947,7 +1953,7 @@ macro_rules! impl_into_func {
match result { match result {
CallResult::Ok(val) => val, CallResult::Ok(val) => val,
CallResult::Trap(err) => Trap::raise(err), CallResult::Trap(err) => crate::trap::raise(err),
CallResult::Panic(panic) => wasmtime_runtime::resume_panic(panic), CallResult::Panic(panic) => wasmtime_runtime::resume_panic(panic),
} }
} }

View File

@@ -3,7 +3,7 @@ use crate::store::{InstanceId, StoreOpaque, Stored};
use crate::types::matching; use crate::types::matching;
use crate::{ use crate::{
AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, SharedMemory, AsContextMut, Engine, Export, Extern, Func, Global, Memory, Module, SharedMemory,
StoreContextMut, Table, Trap, TypedFunc, StoreContextMut, Table, TypedFunc,
}; };
use anyhow::{anyhow, bail, Context, Error, Result}; use anyhow::{anyhow, bail, Context, Error, Result};
use std::mem; use std::mem;
@@ -91,6 +91,8 @@ impl Instance {
/// check for trap errors, you can use `error.downcast::<Trap>()`. For more /// check for trap errors, you can use `error.downcast::<Trap>()`. For more
/// about error handling see the [`Trap`] documentation. /// about error handling see the [`Trap`] documentation.
/// ///
/// [`Trap`]: crate::Trap
///
/// # Panics /// # Panics
/// ///
/// This function will panic if called with a store associated with a /// This function will panic if called with a store associated with a
@@ -325,7 +327,7 @@ impl Instance {
) )
.map_err(|e| -> Error { .map_err(|e| -> Error {
match e { match e {
InstantiationError::Trap(trap) => Trap::from_env(trap).into(), InstantiationError::Trap(trap) => trap.into(),
other => other.into(), other => other.into(),
} }
})?; })?;

View File

@@ -3,14 +3,13 @@
use crate::code::CodeObject; use crate::code::CodeObject;
#[cfg(feature = "component-model")] #[cfg(feature = "component-model")]
use crate::component::Component; use crate::component::Component;
use crate::{FrameInfo, Module}; use crate::{FrameInfo, Module, Trap};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::btree_map::Entry; use std::collections::btree_map::Entry;
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
use wasmtime_environ::TrapCode;
use wasmtime_jit::CodeMemory; use wasmtime_jit::CodeMemory;
use wasmtime_runtime::{ModuleInfo, VMCallerCheckedAnyfunc, VMTrampoline}; use wasmtime_runtime::{ModuleInfo, VMCallerCheckedAnyfunc, VMTrampoline};
@@ -132,7 +131,7 @@ impl ModuleRegistry {
} }
/// Fetches trap information about a program counter in a backtrace. /// Fetches trap information about a program counter in a backtrace.
pub fn lookup_trap_code(&self, pc: usize) -> Option<TrapCode> { pub fn lookup_trap_code(&self, pc: usize) -> Option<Trap> {
let (code, offset) = self.code(pc)?; let (code, offset) = self.code(pc)?;
wasmtime_environ::lookup_trap_code(code.code.code_memory().trap_data(), offset) wasmtime_environ::lookup_trap_code(code.code.code_memory().trap_data(), offset)
} }

View File

@@ -1,6 +1,6 @@
//! Support for a calling of an imported function. //! Support for a calling of an imported function.
use crate::{Engine, FuncType, Trap, ValRaw}; use crate::{Engine, FuncType, ValRaw};
use anyhow::Result; use anyhow::Result;
use std::panic::{self, AssertUnwindSafe}; use std::panic::{self, AssertUnwindSafe};
use std::ptr::NonNull; use std::ptr::NonNull;
@@ -56,7 +56,7 @@ unsafe extern "C" fn stub_fn<F>(
// call-site, which gets unwrapped in `Trap::from_runtime` later on as we // call-site, which gets unwrapped in `Trap::from_runtime` later on as we
// convert from the internal `Trap` type to our own `Trap` type in this // convert from the internal `Trap` type to our own `Trap` type in this
// crate. // crate.
Ok(Err(trap)) => Trap::raise(trap.into()), Ok(Err(trap)) => crate::trap::raise(trap.into()),
// And finally if the imported function panicked, then we trigger the // And finally if the imported function panicked, then we trigger the
// form of unwinding that's safe to jump over wasm code on all // form of unwinding that's safe to jump over wasm code on all

View File

@@ -2,7 +2,7 @@ use crate::store::StoreOpaque;
use crate::Module; use crate::Module;
use anyhow::Error; use anyhow::Error;
use std::fmt; use std::fmt;
use wasmtime_environ::{EntityRef, FilePos, TrapCode}; use wasmtime_environ::{EntityRef, FilePos};
use wasmtime_jit::{demangle_function_name, demangle_function_name_or_index}; use wasmtime_jit::{demangle_function_name, demangle_function_name_or_index};
/// Representation of a WebAssembly trap and what caused it to occur. /// Representation of a WebAssembly trap and what caused it to occur.
@@ -65,78 +65,20 @@ use wasmtime_jit::{demangle_function_name, demangle_function_name_or_index};
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
// pub use wasmtime_environ::Trap;
// The code can be accessed from the c-api, where the possible values are translated
// into enum values defined there:
//
// * `wasm_trap_code` in c-api/src/trap.rs, and
// * `wasmtime_trap_code_enum` in c-api/include/wasmtime/trap.h.
//
// These need to be kept in sync.
#[non_exhaustive]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Trap {
/// The current stack space was exhausted.
StackOverflow,
/// An out-of-bounds memory access. // Same safety requirements and caveats as
MemoryOutOfBounds, // `wasmtime_runtime::raise_user_trap`.
pub(crate) unsafe fn raise(error: anyhow::Error) -> ! {
/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
HeapMisaligned,
/// An out-of-bounds access to a table.
TableOutOfBounds,
/// Indirect call to a null table entry.
IndirectCallToNull,
/// Signature mismatch on indirect call.
BadSignature,
/// An integer arithmetic operation caused an overflow.
IntegerOverflow,
/// An integer division by zero.
IntegerDivisionByZero,
/// Failed float-to-int conversion.
BadConversionToInteger,
/// Code that was supposed to have been unreachable was reached.
UnreachableCodeReached,
/// Execution has potentially run too long and may be interrupted.
Interrupt,
/// When the `component-model` feature is enabled this trap represents a
/// function that was `canon lift`'d, then `canon lower`'d, then called.
/// This combination of creation of a function in the component model
/// generates a function that always traps and, when called, produces this
/// flavor of trap.
AlwaysTrapAdapter,
/// When wasm code is configured to consume fuel and it runs out of fuel
/// then this trap will be raised.
///
/// For more information see
/// [`Config::consume_fuel`](crate::Config::consume_fuel).
OutOfFuel,
}
impl Trap {
// Same safety requirements and caveats as
// `wasmtime_runtime::raise_user_trap`.
pub(crate) unsafe fn raise(error: anyhow::Error) -> ! {
let needs_backtrace = error.downcast_ref::<WasmBacktrace>().is_none(); let needs_backtrace = error.downcast_ref::<WasmBacktrace>().is_none();
wasmtime_runtime::raise_user_trap(error, needs_backtrace) wasmtime_runtime::raise_user_trap(error, needs_backtrace)
} }
#[cold] // traps are exceptional, this helps move handling off the main path #[cold] // traps are exceptional, this helps move handling off the main path
pub(crate) fn from_runtime_box( pub(crate) fn from_runtime_box(
store: &StoreOpaque, store: &StoreOpaque,
runtime_trap: Box<wasmtime_runtime::Trap>, runtime_trap: Box<wasmtime_runtime::Trap>,
) -> Error { ) -> Error {
let wasmtime_runtime::Trap { reason, backtrace } = *runtime_trap; let wasmtime_runtime::Trap { reason, backtrace } = *runtime_trap;
let (error, pc) = match reason { let (error, pc) = match reason {
// For user-defined errors they're already an `anyhow::Error` so no // For user-defined errors they're already an `anyhow::Error` so no
@@ -163,12 +105,10 @@ impl Trap {
let code = store let code = store
.modules() .modules()
.lookup_trap_code(pc) .lookup_trap_code(pc)
.unwrap_or(TrapCode::StackOverflow); .unwrap_or(Trap::StackOverflow);
(Trap::from_env(code).into(), Some(pc)) (code.into(), Some(pc))
}
wasmtime_runtime::TrapReason::Wasm(trap_code) => {
(Trap::from_env(trap_code).into(), None)
} }
wasmtime_runtime::TrapReason::Wasm(trap_code) => (trap_code.into(), None),
}; };
match backtrace { match backtrace {
Some(bt) => { Some(bt) => {
@@ -181,52 +121,8 @@ impl Trap {
} }
None => error, None => error,
} }
}
/// Panics if `code` is `TrapCode::User`.
pub(crate) fn from_env(code: TrapCode) -> Self {
match code {
TrapCode::StackOverflow => Trap::StackOverflow,
TrapCode::HeapOutOfBounds => Trap::MemoryOutOfBounds,
TrapCode::HeapMisaligned => Trap::HeapMisaligned,
TrapCode::TableOutOfBounds => Trap::TableOutOfBounds,
TrapCode::IndirectCallToNull => Trap::IndirectCallToNull,
TrapCode::BadSignature => Trap::BadSignature,
TrapCode::IntegerOverflow => Trap::IntegerOverflow,
TrapCode::IntegerDivisionByZero => Trap::IntegerDivisionByZero,
TrapCode::BadConversionToInteger => Trap::BadConversionToInteger,
TrapCode::UnreachableCodeReached => Trap::UnreachableCodeReached,
TrapCode::Interrupt => Trap::Interrupt,
TrapCode::AlwaysTrapAdapter => Trap::AlwaysTrapAdapter,
}
}
} }
impl fmt::Display for Trap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Trap::*;
let desc = match self {
StackOverflow => "call stack exhausted",
MemoryOutOfBounds => "out of bounds memory access",
HeapMisaligned => "misaligned memory access",
TableOutOfBounds => "undefined element: out of bounds table access",
IndirectCallToNull => "uninitialized element",
BadSignature => "indirect call type mismatch",
IntegerOverflow => "integer overflow",
IntegerDivisionByZero => "integer divide by zero",
BadConversionToInteger => "invalid conversion to integer",
UnreachableCodeReached => "wasm `unreachable` instruction executed",
Interrupt => "interrupt",
AlwaysTrapAdapter => "degenerate component adapter called",
OutOfFuel => "all fuel consumed by WebAssembly",
};
write!(f, "wasm trap: {desc}")
}
}
impl std::error::Error for Trap {}
/// Representation of a backtrace of function frames in a WebAssembly module for /// Representation of a backtrace of function frames in a WebAssembly module for
/// where an error happened. /// where an error happened.
/// ///