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 #[error("module size limit (max_module_size) exceeded: {size} bytes > {max} bytes limit")]
51 ModuleSizeLimitExceeded { size: u64, max: u64 },
52}
53
54#[derive(Debug, Serialize, ThisError)]
55#[serde(tag = "type", content = "data")]
56#[non_exhaustive]
57pub enum MethodResolutionError {
58 #[error("method {name:?} has invalid signature: expected no arguments and no return value")]
59 InvalidSignature { name: String },
60 #[error("method {name:?} not found")]
61 MethodNotFound { name: String },
62 #[error("method name too long: {length} bytes (max: {max})")]
63 MethodNameTooLong {
64 name: String,
65 length: usize,
66 max: u64,
67 },
68 #[error("method name contains invalid character at position {position}: {character:?}")]
69 InvalidMethodNameCharacter {
70 name: String,
71 character: char,
72 position: usize,
73 },
74 #[error("method name is empty")]
75 EmptyMethodName,
76}
77
78#[derive(Debug, Serialize, ThisError)]
79#[serde(tag = "type", content = "data")]
80#[non_exhaustive]
81pub enum HostError {
82 #[error("invalid register id: {id}")]
83 InvalidRegisterId { id: u64 },
84 #[error("invalid memory access")]
85 InvalidMemoryAccess,
86 #[error(
87 "{} panicked: {message}{}",
88 match .context {
89 PanicContext::Guest => "guest",
90 PanicContext::Host => "host",
91 },
92 match .location {
93 Location::Unknown => String::new(),
94 Location::At { file, line, column } => format!(" at {file}:{line}:{column}"),
95 }
96 )]
97 Panic {
98 context: PanicContext,
99 message: String,
100 #[serde(skip_serializing_if = "Location::is_unknown")]
101 location: Location,
102 },
103 #[error("invalid UTF-8 string")]
104 BadUTF8,
105 #[error("deserialization error")]
106 DeserializationError,
107 #[error("serialization error")]
108 SerializationError,
109 #[error("integer overflow")]
110 IntegerOverflow,
111 #[error("key length overflow")]
112 KeyLengthOverflow,
113 #[error("value length overflow")]
114 ValueLengthOverflow,
115 #[error("log size overflow")]
116 LogLengthOverflow,
117 #[error("logs overflow")]
118 LogsOverflow,
119 #[error("events overflow")]
120 EventsOverflow,
121 #[error("event kind size overflow")]
122 EventKindSizeOverflow,
123 #[error("event data size overflow")]
124 EventDataSizeOverflow,
125 #[error("xcalls overflow")]
126 XCallsOverflow,
127 #[error("xcall function size overflow")]
128 XCallFunctionSizeOverflow,
129 #[error("xcall params size overflow")]
130 XCallParamsSizeOverflow,
131 #[error("blob operations not supported (NodeClient not available)")]
132 BlobsNotSupported,
133 #[error("invalid blob handle")]
134 InvalidBlobHandle,
135 #[error("too many blob handles (max: {max})")]
136 TooManyBlobHandles { max: u64 },
137 #[error("blob buffer too large (size: {size}, max: {max})")]
138 BlobBufferTooLarge { size: u64, max: u64 },
139 #[error("total blob memory exceeded (current: {current}, max: {max})")]
140 TotalBlobMemoryExceeded { current: u64, max: u64 },
141 #[error("blob write too large (size: {size}, max: {max})")]
142 BlobWriteTooLarge { size: u64, max: u64 },
143 #[error("context does not have permission to access this blob handle")]
144 BlobContextMismatch,
145 #[error("too many blob handles open")]
146 BlobHandleLimitExceeded,
147 #[error("total blob memory usage exceeds limit")]
148 BlobMemoryLimitExceeded,
149 #[error("incorrect ed25519 public key")]
150 Ed25519IncorrectPublicKey,
151 #[error("alias already exists: {0}")]
152 AliasAlreadyExists(String),
153 #[error("alias too long: {0}")]
154 AliasTooLong(usize),
155 #[error("node client is not available")]
156 NodeClientNotAvailable,
157}
158
159#[derive(Copy, Clone, Debug, Serialize)]
160#[expect(
161 clippy::exhaustive_enums,
162 reason = "There are no more possible variants"
163)]
164pub enum PanicContext {
165 Guest,
166 Host,
167}
168
169#[derive(Copy, Clone, Debug, Serialize, ThisError)]
170#[non_exhaustive]
171pub enum WasmTrap {
172 #[error("stack overflow")]
173 StackOverflow,
174 #[error("memory out of bounds")]
175 MemoryOutOfBounds,
176 #[error("heap misaligned")]
177 HeapMisaligned,
178 #[error("table access out of bounds")]
179 TableAccessOutOfBounds,
180 #[error("indirect call to null")]
181 IndirectCallToNull,
182 #[error("bad signature")]
183 BadSignature,
184 #[error("illegal arithmetic operation")]
185 IllegalArithmetic,
186 #[error("unreachable code reached")]
187 Unreachable,
188 #[error("unaligned atomic operation")]
189 UnalignedAtomic,
190 #[error("indeterminate trap")]
191 Indeterminate,
192}
193
194#[derive(Debug, Serialize)]
195#[serde(untagged)]
196#[non_exhaustive]
197pub enum Location {
198 At {
199 file: String,
200 line: u32,
201 column: u32,
202 },
203 Unknown,
204}
205
206impl Location {
207 const fn is_unknown(&self) -> bool {
208 matches!(self, Self::Unknown)
209 }
210}
211
212impl From<&PanicLocation<'_>> for Location {
213 fn from(location: &PanicLocation<'_>) -> Self {
214 Self::At {
215 file: location.file().to_owned(),
216 line: location.line(),
217 column: location.column(),
218 }
219 }
220}
221
222impl From<ExportError> for FunctionCallError {
223 fn from(err: ExportError) -> Self {
224 match err {
225 ExportError::Missing(name) => {
226 Self::MethodResolutionError(MethodResolutionError::MethodNotFound { name })
227 }
228 ExportError::IncompatibleType => unreachable!(),
229 }
230 }
231}
232
233#[expect(
235 clippy::fallible_impl_from,
236 reason = "TODO: This needs to be refactored"
237)]
238impl From<InstantiationError> for FunctionCallError {
239 fn from(err: InstantiationError) -> Self {
240 match err {
241 InstantiationError::Link(err) => err.into(),
242 InstantiationError::Start(err) => err.into(),
243 InstantiationError::CpuFeature(err) => {
244 panic!("host CPU does not support a required feature: {err}")
245 }
246 InstantiationError::DifferentStores => {
247 panic!("one of the imports is incompatible with this execution instance")
248 }
249 InstantiationError::DifferentArchOS => {
250 panic!("the module was compiled for a different architecture or operating system")
251 }
252 }
253 }
254}
255
256impl From<RuntimeError> for FunctionCallError {
257 fn from(err: RuntimeError) -> Self {
258 match err.to_trap() {
259 Some(TrapCode::StackOverflow) => Self::WasmTrap(WasmTrap::StackOverflow),
260 Some(TrapCode::HeapAccessOutOfBounds | TrapCode::TableAccessOutOfBounds) => {
261 Self::WasmTrap(WasmTrap::MemoryOutOfBounds)
262 }
263 Some(TrapCode::HeapMisaligned) => Self::WasmTrap(WasmTrap::HeapMisaligned),
264 Some(TrapCode::IndirectCallToNull) => Self::WasmTrap(WasmTrap::IndirectCallToNull),
265 Some(TrapCode::BadSignature) => Self::WasmTrap(WasmTrap::BadSignature),
266 Some(
267 TrapCode::IntegerOverflow
268 | TrapCode::IntegerDivisionByZero
269 | TrapCode::BadConversionToInteger,
270 ) => Self::WasmTrap(WasmTrap::IllegalArithmetic),
271 Some(TrapCode::UnreachableCodeReached) => Self::WasmTrap(WasmTrap::Unreachable),
272 Some(TrapCode::UnalignedAtomic) => Self::WasmTrap(WasmTrap::UnalignedAtomic),
273 Some(TrapCode::UncaughtException) => Self::WasmTrap(WasmTrap::Indeterminate),
274 None => Self::WasmTrap(WasmTrap::Indeterminate),
275 }
276 }
277}