hyperlight_host/
error.rs

1/*
2Copyright 2025  The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17use std::array::TryFromSliceError;
18use std::cell::{BorrowError, BorrowMutError};
19use std::convert::Infallible;
20use std::error::Error;
21use std::num::TryFromIntError;
22use std::string::FromUtf8Error;
23use std::sync::{MutexGuard, PoisonError};
24use std::time::SystemTimeError;
25
26#[cfg(target_os = "windows")]
27use crossbeam_channel::{RecvError, SendError};
28use flatbuffers::InvalidFlatbuffer;
29use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue};
30use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
31use thiserror::Error;
32
33#[cfg(target_os = "windows")]
34use crate::hypervisor::wrappers::HandleWrapper;
35use crate::mem::memory_region::MemoryRegionFlags;
36use crate::mem::ptr::RawPtr;
37
38/// The error type for Hyperlight operations
39#[derive(Error, Debug)]
40pub enum HyperlightError {
41    /// Anyhow error
42    #[error("Anyhow Error was returned: {0}")]
43    AnyhowError(#[from] anyhow::Error),
44    /// Memory access out of bounds
45    #[error("Offset: {0} out of bounds, Max is: {1}")]
46    BoundsCheckFailed(u64, usize),
47
48    /// Checked Add Overflow
49    #[error("Couldn't add offset to base address. Offset: {0}, Base Address: {1}")]
50    CheckedAddOverflow(u64, u64),
51
52    /// Cross beam channel receive error
53    #[error("{0:?}")]
54    #[cfg(target_os = "windows")]
55    CrossBeamReceiveError(#[from] RecvError),
56
57    /// Cross beam channel send error
58    #[error("{0:?}")]
59    #[cfg(target_os = "windows")]
60    CrossBeamSendError(#[from] SendError<HandleWrapper>),
61
62    /// CString conversion error
63    #[error("Error converting CString {0:?}")]
64    CStringConversionError(#[from] std::ffi::NulError),
65
66    /// A generic error with a message
67    #[error("{0}")]
68    Error(String),
69
70    /// Execution violation
71    #[error("Non-executable address {0:#x} tried to be executed")]
72    ExecutionAccessViolation(u64),
73
74    /// Guest execution was cancelled by the host
75    #[error("Execution was cancelled by the host.")]
76    ExecutionCanceledByHost(),
77
78    /// Accessing the value of a flatbuffer parameter failed
79    #[error("Failed to get a value from flat buffer parameter")]
80    FailedToGetValueFromParameter(),
81
82    ///Field Name not found in decoded GuestLogData
83    #[error("Field Name {0} not found in decoded GuestLogData")]
84    FieldIsMissingInGuestLogData(String),
85
86    /// Guest aborted during outb
87    #[error("Guest aborted: {0} {1}")]
88    GuestAborted(u8, String),
89
90    /// Guest call resulted in error in guest
91    #[error("Guest error occurred {0:?}: {1}")]
92    GuestError(ErrorCode, String),
93
94    /// An attempt to cancel guest execution failed because it is hanging on a host function call
95    #[error("Guest execution hung on the execution of a host function call")]
96    GuestExecutionHungOnHostFunctionCall(),
97
98    /// Guest call already in progress
99    #[error("Guest call is already in progress")]
100    GuestFunctionCallAlreadyInProgress(),
101
102    /// The given type is not supported by the guest interface.
103    #[error("Unsupported type: {0}")]
104    GuestInterfaceUnsupportedType(String),
105
106    /// The guest offset is invalid.
107    #[error("The guest offset {0} is invalid.")]
108    GuestOffsetIsInvalid(usize),
109
110    /// A Host function was called by the guest but it was not registered.
111    #[error("HostFunction {0} was not found")]
112    HostFunctionNotFound(String),
113
114    /// Reading Writing or Seeking data failed.
115    #[error("Reading Writing or Seeking data failed {0:?}")]
116    IOError(#[from] std::io::Error),
117
118    /// Failed to convert to Integer
119    #[error("Failed To Convert Size to usize")]
120    IntConversionFailure(#[from] TryFromIntError),
121
122    /// The flatbuffer is invalid
123    #[error("The flatbuffer is invalid")]
124    InvalidFlatBuffer(#[from] InvalidFlatbuffer),
125
126    /// Conversion of str to Json failed
127    #[error("Conversion of str data to json failed")]
128    JsonConversionFailure(#[from] serde_json::Error),
129
130    /// KVM Error Occurred
131    #[error("KVM Error {0:?}")]
132    #[cfg(kvm)]
133    KVMError(#[from] kvm_ioctls::Error),
134
135    /// An attempt to get a lock from a Mutex failed.
136    #[error("Unable to lock resource")]
137    LockAttemptFailed(String),
138
139    /// Memory Access Violation at the given address. The access type and memory region flags are provided.
140    #[error("Memory Access Violation at address {0:#x} of type {1}, but memory is marked as {2}")]
141    MemoryAccessViolation(u64, MemoryRegionFlags, MemoryRegionFlags),
142
143    /// Memory Allocation Failed.
144    #[error("Memory Allocation Failed with OS Error {0:?}.")]
145    MemoryAllocationFailed(Option<i32>),
146
147    /// Memory Protection Failed
148    #[error("Memory Protection Failed with OS Error {0:?}.")]
149    MemoryProtectionFailed(Option<i32>),
150
151    /// The memory request exceeds the maximum size allowed
152    #[error("Memory requested {0} exceeds maximum size allowed {1}")]
153    MemoryRequestTooBig(usize, usize),
154
155    /// Metric Not Found.
156    #[error("Metric Not Found {0:?}.")]
157    MetricNotFound(&'static str),
158
159    /// mmap Failed.
160    #[error("mmap failed with os error {0:?}")]
161    MmapFailed(Option<i32>),
162
163    /// mprotect Failed.
164    #[error("mprotect failed with os error {0:?}")]
165    MprotectFailed(Option<i32>),
166
167    /// mshv Error Occurred
168    #[error("mshv Error {0:?}")]
169    #[cfg(mshv3)]
170    MSHVError(#[from] mshv_ioctls::MshvError),
171
172    /// No Hypervisor was found for Sandbox.
173    #[error("No Hypervisor was found for Sandbox")]
174    NoHypervisorFound(),
175
176    /// Restore_state called with no valid snapshot
177    #[error("Restore_state called with no valid snapshot")]
178    NoMemorySnapshot,
179
180    /// Failed to get value from parameter value
181    #[error("Failed To Convert Parameter Value {0:?} to {1:?}")]
182    ParameterValueConversionFailure(ParameterValue, &'static str),
183
184    /// a failure occurred processing a PE file
185    #[error("Failure processing PE File {0:?}")]
186    PEFileProcessingFailure(#[from] goblin::error::Error),
187
188    /// The sandbox becomes **poisoned** when the guest is not run to completion, leaving it in
189    /// an inconsistent state that could compromise memory safety, data integrity, or security.
190    ///
191    /// ### When Does Poisoning Occur?
192    ///
193    /// Poisoning happens when guest execution is interrupted before normal completion:
194    ///
195    /// - **Guest panics or aborts** - When a guest function panics, crashes, or calls `abort()`,
196    ///   the normal cleanup and unwinding process is interrupted
197    /// - **Invalid memory access** - Attempts to read/write/execute memory outside allowed regions
198    /// - **Stack overflow** - Guest exhausts its stack space during execution
199    /// - **Heap exhaustion** - Guest runs out of heap memory
200    /// - **Host-initiated cancellation** - Calling [`InterruptHandle::kill()`] to forcefully
201    ///   terminate an in-progress guest function
202    ///
203    /// ## Recovery
204    ///
205    /// Use [`crate::MultiUseSandbox::restore()`] to recover from a poisoned sandbox.
206    #[error("The sandbox was poisoned")]
207    PoisonedSandbox,
208
209    /// Raw pointer is less than base address
210    #[error("Raw pointer ({0:?}) was less than the base address ({1})")]
211    RawPointerLessThanBaseAddress(RawPtr, u64),
212
213    /// RefCell borrow failed
214    #[error("RefCell borrow failed")]
215    RefCellBorrowFailed(#[from] BorrowError),
216
217    /// RefCell mut borrow failed
218    #[error("RefCell mut borrow failed")]
219    RefCellMutBorrowFailed(#[from] BorrowMutError),
220
221    /// Failed to get value from return value
222    #[error("Failed To Convert Return Value {0:?} to {1:?}")]
223    ReturnValueConversionFailure(ReturnValue, &'static str),
224
225    /// Stack overflow detected in guest
226    #[error("Stack overflow detected")]
227    StackOverflow(),
228
229    /// Tried to restore snapshot to a sandbox that is not the same as the one the snapshot was taken from
230    #[error("Snapshot was taken from a different sandbox")]
231    SnapshotSandboxMismatch,
232
233    /// SystemTimeError
234    #[error("SystemTimeError {0:?}")]
235    SystemTimeError(#[from] SystemTimeError),
236
237    /// Error occurred when translating guest address
238    #[error("An error occurred when translating guest address: {0:?}")]
239    #[cfg(gdb)]
240    TranslateGuestAddress(u64),
241
242    /// Error occurred converting a slice to an array
243    #[error("TryFromSliceError {0:?}")]
244    TryFromSliceError(#[from] TryFromSliceError),
245
246    /// A function was called with an incorrect number of arguments
247    #[error("The number of arguments to the function is wrong: got {0:?} expected {1:?}")]
248    UnexpectedNoOfArguments(usize, usize),
249
250    /// The parameter value type is unexpected
251    #[error("The parameter value type is unexpected got {0:?} expected {1:?}")]
252    UnexpectedParameterValueType(ParameterValue, String),
253
254    /// The return value type is unexpected
255    #[error("The return value type is unexpected got {0:?} expected {1:?}")]
256    UnexpectedReturnValueType(ReturnValue, String),
257
258    /// Slice conversion to UTF8 failed
259    #[error("String Conversion of UTF8 data to str failed")]
260    UTF8StringConversionFailure(#[from] FromUtf8Error),
261
262    /// The capacity of the vector is incorrect
263    #[error(
264        "The capacity of the vector is incorrect. Capacity: {0}, Length: {1}, FlatBuffer Size: {2}"
265    )]
266    VectorCapacityIncorrect(usize, usize, i32),
267
268    /// vmm sys Error Occurred
269    #[error("vmm sys Error {0:?}")]
270    #[cfg(target_os = "linux")]
271    VmmSysError(vmm_sys_util::errno::Error),
272
273    /// Windows Error
274    #[cfg(target_os = "windows")]
275    #[error("Windows API Error Result {0:?}")]
276    WindowsAPIError(#[from] windows_result::Error),
277}
278
279impl From<Infallible> for HyperlightError {
280    fn from(_: Infallible) -> Self {
281        "Impossible as this is an infallible error".into()
282    }
283}
284
285impl From<&str> for HyperlightError {
286    fn from(s: &str) -> Self {
287        HyperlightError::Error(s.to_string())
288    }
289}
290
291impl<T> From<PoisonError<MutexGuard<'_, T>>> for HyperlightError {
292    // Implemented this way rather than passing the error as a source to LockAttemptFailed as that would require
293    // Box<dyn Error + Send + Sync> which is not easy to implement for PoisonError<MutexGuard<'_, T>>
294    // This is a good enough solution and allows use to use the ? operator on lock() calls
295    fn from(e: PoisonError<MutexGuard<'_, T>>) -> Self {
296        let source = match e.source() {
297            Some(s) => s.to_string(),
298            None => String::from(""),
299        };
300        HyperlightError::LockAttemptFailed(source)
301    }
302}
303
304impl HyperlightError {
305    /// Internal helper to determines if the given error has potential to poison the sandbox.
306    ///
307    /// Errors that poison the sandbox are those that can leave the sandbox in an inconsistent
308    /// state where memory, resources, or data structures may be corrupted or leaked. Usually
309    /// due to the guest not running to completion.
310    ///
311    /// If this method returns `true`, the sandbox will be poisoned and all further operations
312    /// will fail until the sandbox is restored from a non-poisoned snapshot using
313    /// [`crate::MultiUseSandbox::restore()`].
314    pub(crate) fn is_poison_error(&self) -> bool {
315        // wildcard _ or matches! not used here purposefully to ensure that new error variants
316        // are explicitly considered for poisoning behavior.
317        match self {
318            // These errors poison the sandbox because they can leave it in an inconsistent state due
319            // to the guest not running to completion.
320            HyperlightError::GuestAborted(_, _)
321            | HyperlightError::ExecutionCanceledByHost()
322            | HyperlightError::PoisonedSandbox
323            | HyperlightError::ExecutionAccessViolation(_)
324            | HyperlightError::StackOverflow()
325            | HyperlightError::MemoryAccessViolation(_, _, _) => true,
326
327            // All other errors do not poison the sandbox.
328            HyperlightError::AnyhowError(_)
329            | HyperlightError::BoundsCheckFailed(_, _)
330            | HyperlightError::CheckedAddOverflow(_, _)
331            | HyperlightError::CStringConversionError(_)
332            | HyperlightError::Error(_)
333            | HyperlightError::FailedToGetValueFromParameter()
334            | HyperlightError::FieldIsMissingInGuestLogData(_)
335            | HyperlightError::GuestError(_, _)
336            | HyperlightError::GuestExecutionHungOnHostFunctionCall()
337            | HyperlightError::GuestFunctionCallAlreadyInProgress()
338            | HyperlightError::GuestInterfaceUnsupportedType(_)
339            | HyperlightError::GuestOffsetIsInvalid(_)
340            | HyperlightError::HostFunctionNotFound(_)
341            | HyperlightError::IOError(_)
342            | HyperlightError::IntConversionFailure(_)
343            | HyperlightError::InvalidFlatBuffer(_)
344            | HyperlightError::JsonConversionFailure(_)
345            | HyperlightError::LockAttemptFailed(_)
346            | HyperlightError::MemoryAllocationFailed(_)
347            | HyperlightError::MemoryProtectionFailed(_)
348            | HyperlightError::MemoryRequestTooBig(_, _)
349            | HyperlightError::MetricNotFound(_)
350            | HyperlightError::MmapFailed(_)
351            | HyperlightError::MprotectFailed(_)
352            | HyperlightError::NoHypervisorFound()
353            | HyperlightError::NoMemorySnapshot
354            | HyperlightError::ParameterValueConversionFailure(_, _)
355            | HyperlightError::PEFileProcessingFailure(_)
356            | HyperlightError::RawPointerLessThanBaseAddress(_, _)
357            | HyperlightError::RefCellBorrowFailed(_)
358            | HyperlightError::RefCellMutBorrowFailed(_)
359            | HyperlightError::ReturnValueConversionFailure(_, _)
360            | HyperlightError::SnapshotSandboxMismatch
361            | HyperlightError::SystemTimeError(_)
362            | HyperlightError::TryFromSliceError(_)
363            | HyperlightError::UnexpectedNoOfArguments(_, _)
364            | HyperlightError::UnexpectedParameterValueType(_, _)
365            | HyperlightError::UnexpectedReturnValueType(_, _)
366            | HyperlightError::UTF8StringConversionFailure(_)
367            | HyperlightError::VectorCapacityIncorrect(_, _, _) => false,
368
369            #[cfg(target_os = "windows")]
370            HyperlightError::CrossBeamReceiveError(_) => false,
371            #[cfg(target_os = "windows")]
372            HyperlightError::CrossBeamSendError(_) => false,
373            #[cfg(target_os = "windows")]
374            HyperlightError::WindowsAPIError(_) => false,
375            #[cfg(target_os = "linux")]
376            HyperlightError::VmmSysError(_) => false,
377            #[cfg(kvm)]
378            HyperlightError::KVMError(_) => false,
379            #[cfg(mshv3)]
380            HyperlightError::MSHVError(_) => false,
381            #[cfg(gdb)]
382            HyperlightError::TranslateGuestAddress(_) => false,
383        }
384    }
385}
386
387/// Creates a `HyperlightError::Error` from a string literal or format string
388#[macro_export]
389macro_rules! new_error {
390    ($msg:literal $(,)?) => {{
391        let __args = std::format_args!($msg);
392        let __err_msg = match __args.as_str() {
393            Some(msg) => String::from(msg),
394            None => std::format!($msg),
395        };
396        $crate::HyperlightError::Error(__err_msg)
397    }};
398    ($fmtstr:expr, $($arg:tt)*) => {{
399           let __err_msg = std::format!($fmtstr, $($arg)*);
400           $crate::error::HyperlightError::Error(__err_msg)
401    }};
402}