Skip to content

Commit bb434a1

Browse files
committed
Add poison state to sandbox to prevent misuse (#931)
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 1bd07e1 commit bb434a1

File tree

3 files changed

+468
-1
lines changed

3 files changed

+468
-1
lines changed

src/hyperlight_host/src/error.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,27 @@ pub enum HyperlightError {
196196
#[error("Failure processing PE File {0:?}")]
197197
PEFileProcessingFailure(#[from] goblin::error::Error),
198198

199+
/// The sandbox becomes **poisoned** when the guest is not run to completion, leaving it in
200+
/// an inconsistent state that could compromise memory safety, data integrity, or security.
201+
///
202+
/// ### When Does Poisoning Occur?
203+
///
204+
/// Poisoning happens when guest execution is interrupted before normal completion:
205+
///
206+
/// - **Guest panics or aborts** - When a guest function panics, crashes, or calls `abort()`,
207+
/// the normal cleanup and unwinding process is interrupted
208+
/// - **Invalid memory access** - Attempts to read/write/execute memory outside allowed regions
209+
/// - **Stack overflow** - Guest exhausts its stack space during execution
210+
/// - **Heap exhaustion** - Guest runs out of heap memory
211+
/// - **Host-initiated cancellation** - Calling [`InterruptHandle::kill()`] to forcefully
212+
/// terminate an in-progress guest function
213+
///
214+
/// ## Recovery
215+
///
216+
/// Use [`crate::MultiUseSandbox::restore()`] to recover from a poisoned sandbox.
217+
#[error("The sandbox was poisoned")]
218+
PoisonedSandbox,
219+
199220
/// Raw pointer is less than base address
200221
#[error("Raw pointer ({0:?}) was less than the base address ({1})")]
201222
RawPointerLessThanBaseAddress(RawPtr, u64),
@@ -301,6 +322,96 @@ impl<T> From<PoisonError<MutexGuard<'_, T>>> for HyperlightError {
301322
}
302323
}
303324

325+
impl HyperlightError {
326+
/// Internal helper to determines if the given error has potential to poison the sandbox.
327+
///
328+
/// Errors that poison the sandbox are those that can leave the sandbox in an inconsistent
329+
/// state where memory, resources, or data structures may be corrupted or leaked. Usually
330+
/// due to the guest not running to completion.
331+
///
332+
/// If this method returns `true`, the sandbox will be poisoned and all further operations
333+
/// will fail until the sandbox is restored from a non-poisoned snapshot using
334+
/// [`crate::MultiUseSandbox::restore()`].
335+
pub(crate) fn is_poison_error(&self) -> bool {
336+
// wildcard _ or matches! not used here purposefully to ensure that new error variants
337+
// are explicitly considered for poisoning behavior.
338+
match self {
339+
// These errors poison the sandbox because they can leave it in an inconsistent state due
340+
// to the guest not running to completion.
341+
HyperlightError::GuestAborted(_, _)
342+
| HyperlightError::ExecutionCanceledByHost()
343+
| HyperlightError::PoisonedSandbox
344+
| HyperlightError::ExecutionAccessViolation(_)
345+
| HyperlightError::StackOverflow()
346+
| HyperlightError::MemoryAccessViolation(_, _, _) => true,
347+
#[cfg(all(feature = "seccomp", target_os = "linux"))]
348+
HyperlightError::DisallowedSyscall => true,
349+
350+
// All other errors do not poison the sandbox.
351+
HyperlightError::AnyhowError(_)
352+
| HyperlightError::BoundsCheckFailed(_, _)
353+
| HyperlightError::CheckedAddOverflow(_, _)
354+
| HyperlightError::CStringConversionError(_)
355+
| HyperlightError::Error(_)
356+
| HyperlightError::FailedToGetValueFromParameter()
357+
| HyperlightError::FieldIsMissingInGuestLogData(_)
358+
| HyperlightError::GuestError(_, _)
359+
| HyperlightError::GuestExecutionHungOnHostFunctionCall()
360+
| HyperlightError::GuestFunctionCallAlreadyInProgress()
361+
| HyperlightError::GuestInterfaceUnsupportedType(_)
362+
| HyperlightError::GuestOffsetIsInvalid(_)
363+
| HyperlightError::HostFunctionNotFound(_)
364+
| HyperlightError::IOError(_)
365+
| HyperlightError::IntConversionFailure(_)
366+
| HyperlightError::InvalidFlatBuffer(_)
367+
| HyperlightError::JsonConversionFailure(_)
368+
| HyperlightError::LockAttemptFailed(_)
369+
| HyperlightError::MemoryAllocationFailed(_)
370+
| HyperlightError::MemoryProtectionFailed(_)
371+
| HyperlightError::MemoryRequestTooBig(_, _)
372+
| HyperlightError::MetricNotFound(_)
373+
| HyperlightError::MmapFailed(_)
374+
| HyperlightError::MprotectFailed(_)
375+
| HyperlightError::NoHypervisorFound()
376+
| HyperlightError::NoMemorySnapshot
377+
| HyperlightError::ParameterValueConversionFailure(_, _)
378+
| HyperlightError::PEFileProcessingFailure(_)
379+
| HyperlightError::RawPointerLessThanBaseAddress(_, _)
380+
| HyperlightError::RefCellBorrowFailed(_)
381+
| HyperlightError::RefCellMutBorrowFailed(_)
382+
| HyperlightError::ReturnValueConversionFailure(_, _)
383+
| HyperlightError::SnapshotSandboxMismatch
384+
| HyperlightError::SystemTimeError(_)
385+
| HyperlightError::TryFromSliceError(_)
386+
| HyperlightError::UnexpectedNoOfArguments(_, _)
387+
| HyperlightError::UnexpectedParameterValueType(_, _)
388+
| HyperlightError::UnexpectedReturnValueType(_, _)
389+
| HyperlightError::UTF8StringConversionFailure(_)
390+
| HyperlightError::VectorCapacityIncorrect(_, _, _) => false,
391+
392+
#[cfg(target_os = "windows")]
393+
HyperlightError::CrossBeamReceiveError(_) => false,
394+
#[cfg(target_os = "windows")]
395+
HyperlightError::CrossBeamSendError(_) => false,
396+
#[cfg(target_os = "windows")]
397+
HyperlightError::WindowsAPIError(_) => false,
398+
#[cfg(target_os = "linux")]
399+
HyperlightError::VmmSysError(_) => false,
400+
#[cfg(kvm)]
401+
HyperlightError::KVMError(_) => false,
402+
#[cfg(mshv)]
403+
HyperlightError::MSHVError(_) => false,
404+
#[cfg(gdb)]
405+
HyperlightError::TranslateGuestAddress(_) => false,
406+
#[cfg(all(feature = "seccomp", target_os = "linux"))]
407+
HyperlightError::SeccompFilterError(_) => false,
408+
409+
#[cfg(all(feature = "seccomp", target_os = "linux"))]
410+
HyperlightError::SeccompFilterBackendError(_) => false,
411+
}
412+
}
413+
}
414+
304415
/// Creates a `HyperlightError::Error` from a string literal or format string
305416
#[macro_export]
306417
macro_rules! new_error {

0 commit comments

Comments
 (0)