Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 56 additions & 0 deletions litebox/src/platform/arch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

//! Architecture-specific platform interfaces.
//!
//! As it currently stands, the interfaces here are only considered for x86-64, in the future other
//! architectures might be supported.

use thiserror::Error;

/// A provider of architecture-specific functionality.
#[cfg(target_arch = "x86_64")]
pub trait ArchSpecificProvider {
/// Get the architecture-specific `reg`, for the current guest context.
///
/// Broadly speaking, the platform may use some architecture-specific registers for its own
/// purposes, and the guest may not be able to directly access or work with them. This function
/// (along with [`Self::set_arch_specific_register`]) provides the special handling for such
/// registers. This allows the shim, on behalf of the guest, consistently handle such registers
/// without needing to worry about platform-specifics.
fn get_arch_specific_register(
&self,
reg: &ArchSpecificRegister,
) -> Result<usize, ArchSpecificError>;

/// Set the architecture-specific `reg` to `val`, for the current guest context.
///
/// See [`Self::get_arch_specific_register`] for details.
fn set_arch_specific_register(
&self,
reg: &ArchSpecificRegister,
val: usize,
) -> Result<(), ArchSpecificError>;
}

/// Architecture-specific registers.
///
/// Implementations of [`ArchSpecificProvider`] can choose to support any subset of these registers,
/// and are not required to support any of them, although this may (unsurprisingly) lead to reduced
/// functionality of certain shims.
#[cfg(target_arch = "x86_64")]
#[non_exhaustive]
pub enum ArchSpecificRegister {
FsBase,
GsBase,
}

/// Errors that can be produced by a [`ArchSpecificProvider`] operation.
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum ArchSpecificError {
#[error("register is (currently) not supported on the platform")]
RegisterUnsupported,
#[error("register is reserved by the platform and access is not allowed")]
RegisterReserved,
}
18 changes: 12 additions & 6 deletions litebox/src/platform/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,19 @@ impl TimeProvider for MockPlatform {
}
}

impl PunchthroughProvider for MockPlatform {
type PunchthroughToken<'a> = trivial_providers::ImpossiblePunchthroughToken;
fn get_punchthrough_token_for<'a>(
impl ArchSpecificProvider for MockPlatform {
fn get_arch_specific_register(
&self,
punchthrough: <Self::PunchthroughToken<'a> as PunchthroughToken>::Punchthrough,
) -> Option<Self::PunchthroughToken<'a>> {
None
_reg: &ArchSpecificRegister,
) -> Result<usize, ArchSpecificError> {
Err(ArchSpecificError::RegisterUnsupported)
}
fn set_arch_specific_register(
&self,
_reg: &ArchSpecificRegister,
_val: usize,
) -> Result<(), ArchSpecificError> {
Err(ArchSpecificError::RegisterUnsupported)
}
}

Expand Down
134 changes: 3 additions & 131 deletions litebox/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
//! trait is merely a collection of subtraits that could be composed independently from various
//! other crates that implement them upon various types.

mod arch;
pub mod common_providers;
pub mod page_mgmt;
pub mod trivial_providers;

#[cfg(test)]
pub(crate) mod mock;

use either::Either;
use thiserror::Error;
use zerocopy::{FromBytes, IntoBytes};

pub use arch::{ArchSpecificError, ArchSpecificProvider, ArchSpecificRegister};
pub use page_mgmt::PageManagementProvider;

/// A provider of a platform upon which LiteBox can execute.
Expand All @@ -26,7 +27,7 @@ pub use page_mgmt::PageManagementProvider;
/// provided by it. _However_, most of the provided APIs within the provider act upon an `&self` to
/// allow storage of any useful "globals" within it necessary.
pub trait Provider:
RawMutexProvider + IPInterfaceProvider + TimeProvider + PunchthroughProvider + RawPointerProvider
RawMutexProvider + IPInterfaceProvider + TimeProvider + ArchSpecificProvider + RawPointerProvider
{
}

Expand Down Expand Up @@ -144,126 +145,6 @@ pub trait SignalProvider {
fn take_pending_signals(&self, f: impl FnMut(Self::Signal)) {}
}

/// Punch through any functionality for a particular platform that is not explicitly part of the
/// common _shared_ platform interface.
///
/// The punchthrough primarily exists to improve auditability, rather than preventing arbitrary
/// calls outside of the common interface, since it is impossible in Rust to prevent arbitrary
/// external calls. Thus, it should not be thought of as a security boundary. However, this should
/// be treated closer to "if someone is invoking things from the host without passing through a
/// punchthrough, their code is suspicious; if all host invocations pass through the punchthrough,
/// then it is sufficient to audit the punchthrough interface".
pub trait PunchthroughProvider {
type PunchthroughToken<'a>: PunchthroughToken;
/// Give permission token to invoke `punchthrough`, possibly after checking that it is ok.
///
/// Even though `&self` is taken shared, the intention with the tokens is to use them
/// _immediately_ before invoking other platform interactions. Ideally, we would ensure this via
/// an `&mut self` to guarantee exclusivity, but this would limit us from supporting the ability
/// for other threads being blocked when a punchthrough is done. Thus, this is kept as a
/// `&self`. Morally this should be viewed as a `&mut self`.
fn get_punchthrough_token_for<'a>(
&self,
punchthrough: <Self::PunchthroughToken<'a> as PunchthroughToken>::Punchthrough,
) -> Option<Self::PunchthroughToken<'a>>;
}

/// A token that demonstrates that the platform is allowing access for a particular [`Punchthrough`]
/// to occur (at that point, or at some indeterminate point in the future).
pub trait PunchthroughToken {
type Punchthrough: Punchthrough;
/// Consume the token, and invoke the underlying punchthrough that it represented.
fn execute(
self,
) -> Result<
<Self::Punchthrough as Punchthrough>::ReturnSuccess,
PunchthroughError<<Self::Punchthrough as Punchthrough>::ReturnFailure>,
>;
}

/// Punchthrough support allowing access to functionality not captured by [`Provider`].
///
/// Ideally, this is implemented by a (possibly `#[non_exhaustive]`) enum where a platform
/// provider can mark any unsupported/unimplemented punchthrough functionality with a
/// [`PunchthroughError::Unsupported`] or [`PunchthroughError::Unimplemented`].
///
/// The `Token` allows for obtaining permission from (and possibly, mutable access to) the platform
pub trait Punchthrough {
type ReturnSuccess;
type ReturnFailure: core::error::Error;
}

/// Possible errors for a [`Punchthrough`]
#[derive(Error, Debug)]
pub enum PunchthroughError<E: core::error::Error> {
#[error("attempted to execute unsupported punchthrough")]
Unsupported,
#[error("punchthrough for `{0}` is not implemented")]
Unimplemented(&'static str),
#[error(transparent)]
Failure(#[from] E),
}

/// An error-implementing [`Either`]-style type.
#[derive(Error, Debug)]
pub enum EitherError<L: core::error::Error, R: core::error::Error> {
#[error(transparent)]
Left(L),
#[error(transparent)]
Right(R),
}

// To support easily composing punchthroughs, it is implemented on the `Either` type on
// punchthroughs. An implementation of punchthrough could follow a similar implementation to
// obtain easy internal composability, but composing across crates providing punchthroughs is
// likely best provided using this `Either` based composition.
impl<L, R> PunchthroughToken for Either<L, R>
where
L: PunchthroughToken,
R: PunchthroughToken,
{
type Punchthrough = Either<L::Punchthrough, R::Punchthrough>;

fn execute(
self,
) -> Result<
<Self::Punchthrough as Punchthrough>::ReturnSuccess,
PunchthroughError<<Self::Punchthrough as Punchthrough>::ReturnFailure>,
> {
match self {
Either::Left(l) => match l.execute() {
Ok(res) => Ok(Either::Left(res)),
Err(PunchthroughError::Unsupported) => Err(PunchthroughError::Unsupported),
Err(PunchthroughError::Unimplemented(e)) => {
Err(PunchthroughError::Unimplemented(e))
}
Err(PunchthroughError::Failure(e)) => {
Err(PunchthroughError::Failure(EitherError::Left(e)))
}
},
Either::Right(r) => match r.execute() {
Ok(res) => Ok(Either::Right(res)),
Err(PunchthroughError::Unsupported) => Err(PunchthroughError::Unsupported),
Err(PunchthroughError::Unimplemented(e)) => {
Err(PunchthroughError::Unimplemented(e))
}
Err(PunchthroughError::Failure(e)) => {
Err(PunchthroughError::Failure(EitherError::Right(e)))
}
},
}
}
}

impl<L, R> Punchthrough for Either<L, R>
where
L: Punchthrough,
R: Punchthrough,
{
type ReturnSuccess = Either<L::ReturnSuccess, R::ReturnSuccess>;
type ReturnFailure = EitherError<L::ReturnFailure, R::ReturnFailure>;
}

/// A provider of raw mutexes
pub trait RawMutexProvider {
type RawMutex: RawMutex;
Expand Down Expand Up @@ -675,15 +556,6 @@ pub unsafe trait ThreadLocalStorageProvider {
/// This can be achieved by using
/// [`shim_thread_local!`](crate::shim_thread_local).
unsafe fn replace_thread_local_storage(value: *mut ()) -> *mut ();

/// Clear any guest thread-local storage state for the current thread.
///
/// This is used to help emulate certain syscalls (e.g., `execve`) that clear TLS.
///
/// TODO: move this to a separate trait or eliminate.
fn clear_guest_thread_local_storage() {
unimplemented!()
}
}

/// A provider of cryptographically-secure random data.
Expand Down
Loading
Loading