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