Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/trap_encoding.rs
3067 views
1
use core::fmt;
2
use object::{Bytes, LittleEndian, U32Bytes};
3
4
/// Information about trap.
5
#[derive(Debug, PartialEq, Eq, Clone)]
6
pub struct TrapInformation {
7
/// The offset of the trapping instruction in native code.
8
///
9
/// This is relative to the beginning of the function.
10
pub code_offset: u32,
11
12
/// Code of the trap.
13
pub trap_code: Trap,
14
}
15
16
// The code can be accessed from the c-api, where the possible values are
17
// translated into enum values defined there:
18
//
19
// * the const assertions in c-api/src/trap.rs, and
20
// * `wasmtime_trap_code_enum` in c-api/include/wasmtime/trap.h.
21
//
22
// These need to be kept in sync.
23
#[non_exhaustive]
24
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
25
#[expect(missing_docs, reason = "self-describing variants")]
26
pub enum Trap {
27
/// The current stack space was exhausted.
28
StackOverflow,
29
30
/// An out-of-bounds memory access.
31
MemoryOutOfBounds,
32
33
/// A wasm atomic operation was presented with a not-naturally-aligned linear-memory address.
34
HeapMisaligned,
35
36
/// An out-of-bounds access to a table.
37
TableOutOfBounds,
38
39
/// Indirect call to a null table entry.
40
IndirectCallToNull,
41
42
/// Signature mismatch on indirect call.
43
BadSignature,
44
45
/// An integer arithmetic operation caused an overflow.
46
IntegerOverflow,
47
48
/// An integer division by zero.
49
IntegerDivisionByZero,
50
51
/// Failed float-to-int conversion.
52
BadConversionToInteger,
53
54
/// Code that was supposed to have been unreachable was reached.
55
UnreachableCodeReached,
56
57
/// Execution has potentially run too long and may be interrupted.
58
Interrupt,
59
60
/// When wasm code is configured to consume fuel and it runs out of fuel
61
/// then this trap will be raised.
62
OutOfFuel,
63
64
/// Used to indicate that a trap was raised by atomic wait operations on non shared memory.
65
AtomicWaitNonSharedMemory,
66
67
/// Call to a null reference.
68
NullReference,
69
70
/// Attempt to access beyond the bounds of an array.
71
ArrayOutOfBounds,
72
73
/// Attempted an allocation that was too large to succeed.
74
AllocationTooLarge,
75
76
/// Attempted to cast a reference to a type that it is not an instance of.
77
CastFailure,
78
79
/// When the `component-model` feature is enabled this trap represents a
80
/// scenario where one component tried to call another component but it
81
/// would have violated the reentrance rules of the component model,
82
/// triggering a trap instead.
83
CannotEnterComponent,
84
85
/// Async-lifted export failed to produce a result by calling `task.return`
86
/// before returning `STATUS_DONE` and/or after all host tasks completed.
87
NoAsyncResult,
88
89
/// We are suspending to a tag for which there is no active handler.
90
UnhandledTag,
91
92
/// Attempt to resume a continuation twice.
93
ContinuationAlreadyConsumed,
94
95
/// A Pulley opcode was executed at runtime when the opcode was disabled at
96
/// compile time.
97
DisabledOpcode,
98
99
/// Async event loop deadlocked; i.e. it cannot make further progress given
100
/// that all host tasks have completed and any/all host-owned stream/future
101
/// handles have been dropped.
102
AsyncDeadlock,
103
104
/// When the `component-model` feature is enabled this trap represents a
105
/// scenario where a component instance tried to call an import or intrinsic
106
/// when it wasn't allowed to, e.g. from a post-return function.
107
CannotLeaveComponent,
108
109
/// A synchronous task attempted to make a potentially blocking call prior
110
/// to returning.
111
CannotBlockSyncTask,
112
113
/// A component tried to lift a `char` with an invalid bit pattern.
114
InvalidChar,
115
116
/// Debug assertion generated for a fused adapter regarding the expected
117
/// completion of a string encoding operation.
118
DebugAssertStringEncodingFinished,
119
120
/// Debug assertion generated for a fused adapter regarding a string
121
/// encoding operation.
122
DebugAssertEqualCodeUnits,
123
124
/// Debug assertion generated for a fused adapter regarding the alignment of
125
/// a pointer.
126
DebugAssertPointerAligned,
127
128
/// Debug assertion generated for a fused adapter regarding the upper bits
129
/// of a 64-bit value.
130
DebugAssertUpperBitsUnset,
131
132
/// A component tried to lift or lower a string past the end of its memory.
133
StringOutOfBounds,
134
135
/// A component tried to lift or lower a list past the end of its memory.
136
ListOutOfBounds,
137
138
/// A component used an invalid discriminant when lowering a variant value.
139
InvalidDiscriminant,
140
141
/// A component passed an unaligned pointer when lifting or lowering a
142
/// value.
143
UnalignedPointer,
144
// if adding a variant here be sure to update the `check!` macro below, and
145
// remember to update `trap.rs` and `trap.h` as mentioned above
146
}
147
148
impl Trap {
149
/// Converts a byte back into a `Trap` if its in-bounds
150
pub fn from_u8(byte: u8) -> Option<Trap> {
151
// FIXME: this could use some sort of derive-like thing to avoid having to
152
// deduplicate the names here.
153
//
154
// This simply converts from the a `u8`, to the `Trap` enum.
155
macro_rules! check {
156
($($name:ident)*) => ($(if byte == Trap::$name as u8 {
157
return Some(Trap::$name);
158
})*);
159
}
160
161
check! {
162
StackOverflow
163
MemoryOutOfBounds
164
HeapMisaligned
165
TableOutOfBounds
166
IndirectCallToNull
167
BadSignature
168
IntegerOverflow
169
IntegerDivisionByZero
170
BadConversionToInteger
171
UnreachableCodeReached
172
Interrupt
173
OutOfFuel
174
AtomicWaitNonSharedMemory
175
NullReference
176
ArrayOutOfBounds
177
AllocationTooLarge
178
CastFailure
179
CannotEnterComponent
180
NoAsyncResult
181
UnhandledTag
182
ContinuationAlreadyConsumed
183
DisabledOpcode
184
AsyncDeadlock
185
CannotLeaveComponent
186
CannotBlockSyncTask
187
InvalidChar
188
DebugAssertStringEncodingFinished
189
DebugAssertEqualCodeUnits
190
DebugAssertPointerAligned
191
DebugAssertUpperBitsUnset
192
StringOutOfBounds
193
ListOutOfBounds
194
InvalidDiscriminant
195
UnalignedPointer
196
}
197
198
None
199
}
200
}
201
202
impl fmt::Display for Trap {
203
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204
use Trap::*;
205
206
let desc = match self {
207
StackOverflow => "call stack exhausted",
208
MemoryOutOfBounds => "out of bounds memory access",
209
HeapMisaligned => "unaligned atomic",
210
TableOutOfBounds => "undefined element: out of bounds table access",
211
IndirectCallToNull => "uninitialized element",
212
BadSignature => "indirect call type mismatch",
213
IntegerOverflow => "integer overflow",
214
IntegerDivisionByZero => "integer divide by zero",
215
BadConversionToInteger => "invalid conversion to integer",
216
UnreachableCodeReached => "wasm `unreachable` instruction executed",
217
Interrupt => "interrupt",
218
OutOfFuel => "all fuel consumed by WebAssembly",
219
AtomicWaitNonSharedMemory => "atomic wait on non-shared memory",
220
NullReference => "null reference",
221
ArrayOutOfBounds => "out of bounds array access",
222
AllocationTooLarge => "allocation size too large",
223
CastFailure => "cast failure",
224
CannotEnterComponent => "cannot enter component instance",
225
NoAsyncResult => "async-lifted export failed to produce a result",
226
UnhandledTag => "unhandled tag",
227
ContinuationAlreadyConsumed => "continuation already consumed",
228
DisabledOpcode => "pulley opcode disabled at compile time was executed",
229
AsyncDeadlock => "deadlock detected: event loop cannot make further progress",
230
CannotLeaveComponent => "cannot leave component instance",
231
CannotBlockSyncTask => "cannot block a synchronous task before returning",
232
InvalidChar => "invalid `char` bit pattern",
233
DebugAssertStringEncodingFinished => "should have finished string encoding",
234
DebugAssertEqualCodeUnits => "code units should be equal",
235
DebugAssertPointerAligned => "pointer should be aligned",
236
DebugAssertUpperBitsUnset => "upper bits should be unset",
237
StringOutOfBounds => "string content out-of-bounds",
238
ListOutOfBounds => "list content out-of-bounds",
239
InvalidDiscriminant => "invalid variant discriminant",
240
UnalignedPointer => "unaligned pointer",
241
};
242
write!(f, "wasm trap: {desc}")
243
}
244
}
245
246
impl core::error::Error for Trap {}
247
248
/// Decodes the provided trap information section and attempts to find the trap
249
/// code corresponding to the `offset` specified.
250
///
251
/// The `section` provided is expected to have been built by
252
/// `TrapEncodingBuilder` above. Additionally the `offset` should be a relative
253
/// offset within the text section of the compilation image.
254
pub fn lookup_trap_code(section: &[u8], offset: usize) -> Option<Trap> {
255
let (offsets, traps) = parse(section)?;
256
257
// The `offsets` table is sorted in the trap section so perform a binary
258
// search of the contents of this section to find whether `offset` is an
259
// entry in the section. Note that this is a precise search because trap pcs
260
// should always be precise as well as our metadata about them, which means
261
// we expect an exact match to correspond to a trap opcode.
262
//
263
// Once an index is found within the `offsets` array then that same index is
264
// used to lookup from the `traps` list of bytes to get the trap code byte
265
// corresponding to this offset.
266
let offset = u32::try_from(offset).ok()?;
267
let index = offsets
268
.binary_search_by_key(&offset, |val| val.get(LittleEndian))
269
.ok()?;
270
debug_assert!(index < traps.len());
271
let byte = *traps.get(index)?;
272
273
let trap = Trap::from_u8(byte);
274
debug_assert!(trap.is_some(), "missing mapping for {byte}");
275
trap
276
}
277
278
fn parse(section: &[u8]) -> Option<(&[U32Bytes<LittleEndian>], &[u8])> {
279
let mut section = Bytes(section);
280
// NB: this matches the encoding written by `append_to` above.
281
let count = section.read::<U32Bytes<LittleEndian>>().ok()?;
282
let count = usize::try_from(count.get(LittleEndian)).ok()?;
283
let (offsets, traps) =
284
object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?;
285
debug_assert_eq!(traps.len(), count);
286
Some((offsets, traps))
287
}
288
289
/// Returns an iterator over all of the traps encoded in `section`, which should
290
/// have been produced by `TrapEncodingBuilder`.
291
pub fn iterate_traps(section: &[u8]) -> Option<impl Iterator<Item = (u32, Trap)> + '_> {
292
let (offsets, traps) = parse(section)?;
293
Some(
294
offsets
295
.iter()
296
.zip(traps)
297
.map(|(offset, trap)| (offset.get(LittleEndian), Trap::from_u8(*trap).unwrap())),
298
)
299
}
300
301