Path: blob/main/crates/environ/src/trap_encoding.rs
3067 views
use core::fmt;1use object::{Bytes, LittleEndian, U32Bytes};23/// Information about trap.4#[derive(Debug, PartialEq, Eq, Clone)]5pub struct TrapInformation {6/// The offset of the trapping instruction in native code.7///8/// This is relative to the beginning of the function.9pub code_offset: u32,1011/// Code of the trap.12pub trap_code: Trap,13}1415// The code can be accessed from the c-api, where the possible values are16// translated into enum values defined there:17//18// * the const assertions in c-api/src/trap.rs, and19// * `wasmtime_trap_code_enum` in c-api/include/wasmtime/trap.h.20//21// These need to be kept in sync.22#[non_exhaustive]23#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]24#[expect(missing_docs, reason = "self-describing variants")]25pub enum Trap {26/// The current stack space was exhausted.27StackOverflow,2829/// An out-of-bounds memory access.30MemoryOutOfBounds,3132/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.33HeapMisaligned,3435/// An out-of-bounds access to a table.36TableOutOfBounds,3738/// Indirect call to a null table entry.39IndirectCallToNull,4041/// Signature mismatch on indirect call.42BadSignature,4344/// An integer arithmetic operation caused an overflow.45IntegerOverflow,4647/// An integer division by zero.48IntegerDivisionByZero,4950/// Failed float-to-int conversion.51BadConversionToInteger,5253/// Code that was supposed to have been unreachable was reached.54UnreachableCodeReached,5556/// Execution has potentially run too long and may be interrupted.57Interrupt,5859/// When wasm code is configured to consume fuel and it runs out of fuel60/// then this trap will be raised.61OutOfFuel,6263/// Used to indicate that a trap was raised by atomic wait operations on non shared memory.64AtomicWaitNonSharedMemory,6566/// Call to a null reference.67NullReference,6869/// Attempt to access beyond the bounds of an array.70ArrayOutOfBounds,7172/// Attempted an allocation that was too large to succeed.73AllocationTooLarge,7475/// Attempted to cast a reference to a type that it is not an instance of.76CastFailure,7778/// When the `component-model` feature is enabled this trap represents a79/// scenario where one component tried to call another component but it80/// would have violated the reentrance rules of the component model,81/// triggering a trap instead.82CannotEnterComponent,8384/// Async-lifted export failed to produce a result by calling `task.return`85/// before returning `STATUS_DONE` and/or after all host tasks completed.86NoAsyncResult,8788/// We are suspending to a tag for which there is no active handler.89UnhandledTag,9091/// Attempt to resume a continuation twice.92ContinuationAlreadyConsumed,9394/// A Pulley opcode was executed at runtime when the opcode was disabled at95/// compile time.96DisabledOpcode,9798/// Async event loop deadlocked; i.e. it cannot make further progress given99/// that all host tasks have completed and any/all host-owned stream/future100/// handles have been dropped.101AsyncDeadlock,102103/// When the `component-model` feature is enabled this trap represents a104/// scenario where a component instance tried to call an import or intrinsic105/// when it wasn't allowed to, e.g. from a post-return function.106CannotLeaveComponent,107108/// A synchronous task attempted to make a potentially blocking call prior109/// to returning.110CannotBlockSyncTask,111112/// A component tried to lift a `char` with an invalid bit pattern.113InvalidChar,114115/// Debug assertion generated for a fused adapter regarding the expected116/// completion of a string encoding operation.117DebugAssertStringEncodingFinished,118119/// Debug assertion generated for a fused adapter regarding a string120/// encoding operation.121DebugAssertEqualCodeUnits,122123/// Debug assertion generated for a fused adapter regarding the alignment of124/// a pointer.125DebugAssertPointerAligned,126127/// Debug assertion generated for a fused adapter regarding the upper bits128/// of a 64-bit value.129DebugAssertUpperBitsUnset,130131/// A component tried to lift or lower a string past the end of its memory.132StringOutOfBounds,133134/// A component tried to lift or lower a list past the end of its memory.135ListOutOfBounds,136137/// A component used an invalid discriminant when lowering a variant value.138InvalidDiscriminant,139140/// A component passed an unaligned pointer when lifting or lowering a141/// value.142UnalignedPointer,143// if adding a variant here be sure to update the `check!` macro below, and144// remember to update `trap.rs` and `trap.h` as mentioned above145}146147impl Trap {148/// Converts a byte back into a `Trap` if its in-bounds149pub fn from_u8(byte: u8) -> Option<Trap> {150// FIXME: this could use some sort of derive-like thing to avoid having to151// deduplicate the names here.152//153// This simply converts from the a `u8`, to the `Trap` enum.154macro_rules! check {155($($name:ident)*) => ($(if byte == Trap::$name as u8 {156return Some(Trap::$name);157})*);158}159160check! {161StackOverflow162MemoryOutOfBounds163HeapMisaligned164TableOutOfBounds165IndirectCallToNull166BadSignature167IntegerOverflow168IntegerDivisionByZero169BadConversionToInteger170UnreachableCodeReached171Interrupt172OutOfFuel173AtomicWaitNonSharedMemory174NullReference175ArrayOutOfBounds176AllocationTooLarge177CastFailure178CannotEnterComponent179NoAsyncResult180UnhandledTag181ContinuationAlreadyConsumed182DisabledOpcode183AsyncDeadlock184CannotLeaveComponent185CannotBlockSyncTask186InvalidChar187DebugAssertStringEncodingFinished188DebugAssertEqualCodeUnits189DebugAssertPointerAligned190DebugAssertUpperBitsUnset191StringOutOfBounds192ListOutOfBounds193InvalidDiscriminant194UnalignedPointer195}196197None198}199}200201impl fmt::Display for Trap {202fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {203use Trap::*;204205let desc = match self {206StackOverflow => "call stack exhausted",207MemoryOutOfBounds => "out of bounds memory access",208HeapMisaligned => "unaligned atomic",209TableOutOfBounds => "undefined element: out of bounds table access",210IndirectCallToNull => "uninitialized element",211BadSignature => "indirect call type mismatch",212IntegerOverflow => "integer overflow",213IntegerDivisionByZero => "integer divide by zero",214BadConversionToInteger => "invalid conversion to integer",215UnreachableCodeReached => "wasm `unreachable` instruction executed",216Interrupt => "interrupt",217OutOfFuel => "all fuel consumed by WebAssembly",218AtomicWaitNonSharedMemory => "atomic wait on non-shared memory",219NullReference => "null reference",220ArrayOutOfBounds => "out of bounds array access",221AllocationTooLarge => "allocation size too large",222CastFailure => "cast failure",223CannotEnterComponent => "cannot enter component instance",224NoAsyncResult => "async-lifted export failed to produce a result",225UnhandledTag => "unhandled tag",226ContinuationAlreadyConsumed => "continuation already consumed",227DisabledOpcode => "pulley opcode disabled at compile time was executed",228AsyncDeadlock => "deadlock detected: event loop cannot make further progress",229CannotLeaveComponent => "cannot leave component instance",230CannotBlockSyncTask => "cannot block a synchronous task before returning",231InvalidChar => "invalid `char` bit pattern",232DebugAssertStringEncodingFinished => "should have finished string encoding",233DebugAssertEqualCodeUnits => "code units should be equal",234DebugAssertPointerAligned => "pointer should be aligned",235DebugAssertUpperBitsUnset => "upper bits should be unset",236StringOutOfBounds => "string content out-of-bounds",237ListOutOfBounds => "list content out-of-bounds",238InvalidDiscriminant => "invalid variant discriminant",239UnalignedPointer => "unaligned pointer",240};241write!(f, "wasm trap: {desc}")242}243}244245impl core::error::Error for Trap {}246247/// Decodes the provided trap information section and attempts to find the trap248/// code corresponding to the `offset` specified.249///250/// The `section` provided is expected to have been built by251/// `TrapEncodingBuilder` above. Additionally the `offset` should be a relative252/// offset within the text section of the compilation image.253pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option<Trap> {254let (offsets, traps) = parse(section)?;255256// The `offsets` table is sorted in the trap section so perform a binary257// search of the contents of this section to find whether `offset` is an258// entry in the section. Note that this is a precise search because trap pcs259// should always be precise as well as our metadata about them, which means260// we expect an exact match to correspond to a trap opcode.261//262// Once an index is found within the `offsets` array then that same index is263// used to lookup from the `traps` list of bytes to get the trap code byte264// corresponding to this offset.265let offset = u32::try_from(offset).ok()?;266let index = offsets267.binary_search_by_key(&offset, |val| val.get(LittleEndian))268.ok()?;269debug_assert!(index < traps.len());270let byte = *traps.get(index)?;271272let trap = Trap::from_u8(byte);273debug_assert!(trap.is_some(), "missing mapping for {byte}");274trap275}276277fn parse(section: &[u8]) -> Option<(&[U32Bytes<LittleEndian>], &[u8])> {278let mut section = Bytes(section);279// NB: this matches the encoding written by `append_to` above.280let count = section.read::<U32Bytes<LittleEndian>>().ok()?;281let count = usize::try_from(count.get(LittleEndian)).ok()?;282let (offsets, traps) =283object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;284debug_assert_eq!(traps.len(), count);285Some((offsets, traps))286}287288/// Returns an iterator over all of the traps encoded in `section`, which should289/// have been produced by `TrapEncodingBuilder`.290pub fn iterate_traps(section: &[u8]) -> Option<impl Iterator<Item = (u32, Trap)> + '_> {291let (offsets, traps) = parse(section)?;292Some(293offsets294.iter()295.zip(traps)296.map(|(offset, trap)| (offset.get(LittleEndian), Trap::from_u8(*trap).unwrap())),297)298}299300301