1#[cfg(test)]
2#[path = "tests/errors.rs"]
3mod tests;
4
5use core::panic::Location as PanicLocation;
6
7use serde::Serialize;
8use thiserror::Error as ThisError;
9use wasmer::{ExportError, InstantiationError, LinkError, RuntimeError};
10use wasmer_types::{CompileError, TrapCode};
11
12#[derive(Debug, ThisError)]
13#[non_exhaustive]
14pub enum VMRuntimeError {
15 #[error(transparent)]
16 StorageError(StorageError),
17
18 #[error(transparent)]
19 HostError(HostError),
20}
21
22#[derive(Copy, Clone, Debug, ThisError)]
23#[non_exhaustive]
24pub enum StorageError {}
25
26#[derive(Debug, Serialize, ThisError)]
27#[serde(tag = "type", content = "data")]
28#[non_exhaustive]
29pub enum FunctionCallError {
30 #[error("compilation error: {}", .source)]
31 CompilationError {
32 #[from]
33 #[serde(skip)]
34 source: CompileError,
35 },
36 #[error("link error: {}", .source)]
37 LinkError {
38 #[from]
39 #[serde(skip)]
40 source: LinkError,
41 },
42 #[error(transparent)]
43 MethodResolutionError(MethodResolutionError),
44 #[error(transparent)]
45 WasmTrap(WasmTrap),
46 #[error(transparent)]
47 HostError(HostError),
48 #[error("the method call returned an error: {0:?}")]
49 ExecutionError(Vec<u8>),
50}
51
52#[derive(Debug, Serialize, ThisError)]
53#[serde(tag = "type", content = "data")]
54#[non_exhaustive]
55pub enum MethodResolutionError {
56 #[error("method {name:?} has invalid signature: expected no arguments and no return value")]
57 InvalidSignature { name: String },
58 #[error("method {name:?} not found")]
59 MethodNotFound { name: String },
60}
61
62#[derive(Debug, Serialize, ThisError)]
63#[serde(tag = "type", content = "data")]
64#[non_exhaustive]
65pub enum HostError {
66 #[error("invalid register id: {id}")]
67 InvalidRegisterId { id: u64 },
68 #[error("invalid memory access")]
69 InvalidMemoryAccess,
70 #[error(
71 "{} panicked: {message}{}",
72 match .context {
73 PanicContext::Guest => "guest",
74 PanicContext::Host => "host",
75 },
76 match .location {
77 Location::Unknown => String::new(),
78 Location::At { file, line, column } => format!(" at {file}:{line}:{column}"),
79 }
80 )]
81 Panic {
82 context: PanicContext,
83 message: String,
84 #[serde(skip_serializing_if = "Location::is_unknown")]
85 location: Location,
86 },
87 #[error("invalid UTF-8 string")]
88 BadUTF8,
89 #[error("deserialization error")]
90 DeserializationError,
91 #[error("integer overflow")]
92 IntegerOverflow,
93 #[error("key length overflow")]
94 KeyLengthOverflow,
95 #[error("value length overflow")]
96 ValueLengthOverflow,
97 #[error("logs overflow")]
98 LogsOverflow,
99 #[error("events overflow")]
100 EventsOverflow,
101 #[error("event kind size overflow")]
102 EventKindSizeOverflow,
103 #[error("event data size overflow")]
104 EventDataSizeOverflow,
105 #[error("blob operations not supported (NodeClient not available)")]
106 BlobsNotSupported,
107 #[error("invalid blob handle")]
108 InvalidBlobHandle,
109 #[error("too many blob handles (max: {max})")]
110 TooManyBlobHandles { max: u64 },
111 #[error("blob buffer too large (size: {size}, max: {max})")]
112 BlobBufferTooLarge { size: u64, max: u64 },
113 #[error("total blob memory exceeded (current: {current}, max: {max})")]
114 TotalBlobMemoryExceeded { current: u64, max: u64 },
115 #[error("blob write too large (size: {size}, max: {max})")]
116 BlobWriteTooLarge { size: u64, max: u64 },
117 #[error("context does not have permission to access this blob handle")]
118 BlobContextMismatch,
119 #[error("too many blob handles open")]
120 BlobHandleLimitExceeded,
121 #[error("total blob memory usage exceeds limit")]
122 BlobMemoryLimitExceeded,
123}
124
125#[derive(Copy, Clone, Debug, Serialize)]
126#[expect(
127 clippy::exhaustive_enums,
128 reason = "There are no more possible variants"
129)]
130pub enum PanicContext {
131 Guest,
132 Host,
133}
134
135#[derive(Copy, Clone, Debug, Serialize, ThisError)]
136#[non_exhaustive]
137pub enum WasmTrap {
138 #[error("stack overflow")]
139 StackOverflow,
140 #[error("memory out of bounds")]
141 MemoryOutOfBounds,
142 #[error("heap misaligned")]
143 HeapMisaligned,
144 #[error("table access out of bounds")]
145 TableAccessOutOfBounds,
146 #[error("indirect call to null")]
147 IndirectCallToNull,
148 #[error("bad signature")]
149 BadSignature,
150 #[error("illegal arithmetic operation")]
151 IllegalArithmetic,
152 #[error("unreachable code reached")]
153 Unreachable,
154 #[error("unaligned atomic operation")]
155 UnalignedAtomic,
156 #[error("indeterminate trap")]
157 Indeterminate,
158}
159
160#[derive(Debug, Serialize)]
161#[serde(untagged)]
162#[non_exhaustive]
163pub enum Location {
164 At {
165 file: String,
166 line: u32,
167 column: u32,
168 },
169 Unknown,
170}
171
172impl Location {
173 const fn is_unknown(&self) -> bool {
174 matches!(self, Self::Unknown)
175 }
176}
177
178impl From<&PanicLocation<'_>> for Location {
179 fn from(location: &PanicLocation<'_>) -> Self {
180 Self::At {
181 file: location.file().to_owned(),
182 line: location.line(),
183 column: location.column(),
184 }
185 }
186}
187
188impl From<ExportError> for FunctionCallError {
189 fn from(err: ExportError) -> Self {
190 match err {
191 ExportError::Missing(name) => {
192 Self::MethodResolutionError(MethodResolutionError::MethodNotFound { name })
193 }
194 ExportError::IncompatibleType => unreachable!(),
195 }
196 }
197}
198
199#[expect(
201 clippy::fallible_impl_from,
202 reason = "TODO: This needs to be refactored"
203)]
204impl From<InstantiationError> for FunctionCallError {
205 fn from(err: InstantiationError) -> Self {
206 match err {
207 InstantiationError::Link(err) => err.into(),
208 InstantiationError::Start(err) => err.into(),
209 InstantiationError::CpuFeature(err) => {
210 panic!("host CPU does not support a required feature: {err}")
211 }
212 InstantiationError::DifferentStores => {
213 panic!("one of the imports is incompatible with this execution instance")
214 }
215 InstantiationError::DifferentArchOS => {
216 panic!("the module was compiled for a different architecture or operating system")
217 }
218 }
219 }
220}
221
222impl From<RuntimeError> for FunctionCallError {
223 fn from(err: RuntimeError) -> Self {
224 match err.to_trap() {
225 Some(TrapCode::StackOverflow) => Self::WasmTrap(WasmTrap::StackOverflow),
226 Some(TrapCode::HeapAccessOutOfBounds | TrapCode::TableAccessOutOfBounds) => {
227 Self::WasmTrap(WasmTrap::MemoryOutOfBounds)
228 }
229 Some(TrapCode::HeapMisaligned) => Self::WasmTrap(WasmTrap::HeapMisaligned),
230 Some(TrapCode::IndirectCallToNull) => Self::WasmTrap(WasmTrap::IndirectCallToNull),
231 Some(TrapCode::BadSignature) => Self::WasmTrap(WasmTrap::BadSignature),
232 Some(
233 TrapCode::IntegerOverflow
234 | TrapCode::IntegerDivisionByZero
235 | TrapCode::BadConversionToInteger,
236 ) => Self::WasmTrap(WasmTrap::IllegalArithmetic),
237 Some(TrapCode::UnreachableCodeReached) => Self::WasmTrap(WasmTrap::Unreachable),
238 Some(TrapCode::UnalignedAtomic) => Self::WasmTrap(WasmTrap::UnalignedAtomic),
239 None => Self::WasmTrap(WasmTrap::Indeterminate),
240 }
241 }
242}