From 8f4da919603a8819adbae0b9f156b2edc3fc6d37 Mon Sep 17 00:00:00 2001 From: Jay Bosamiya Date: Tue, 21 Apr 2026 15:01:13 -0700 Subject: [PATCH] Add arch-specific provider; remove PunchthroughProvider --- Cargo.lock | 21 --- litebox/src/platform/arch.rs | 48 +++++++ litebox/src/platform/mock.rs | 18 ++- litebox/src/platform/mod.rs | 134 +------------------ litebox/src/platform/trivial_providers.rs | 98 +------------- litebox_common_linux/Cargo.toml | 1 - litebox_common_linux/src/errno/mod.rs | 16 +++ litebox_common_linux/src/lib.rs | 67 +--------- litebox_platform_linux_kernel/src/lib.rs | 69 ++++------ litebox_platform_linux_userland/src/lib.rs | 73 +++++----- litebox_platform_lvbs/src/lib.rs | 66 ++++----- litebox_platform_windows_userland/src/lib.rs | 60 ++++----- litebox_shim_linux/src/syscalls/process.rs | 37 ++--- litebox_shim_optee/src/lib.rs | 14 +- 14 files changed, 219 insertions(+), 503 deletions(-) create mode 100644 litebox/src/platform/arch.rs diff --git a/Cargo.lock b/Cargo.lock index ad0c763ca..dc73d88de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -183,26 +183,6 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" -[[package]] -name = "bitfield" -version = "0.19.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf79f42d21f18b5926a959280215903e659760da994835d27c3a0c5ff4f898f" -dependencies = [ - "bitfield-macros", -] - -[[package]] -name = "bitfield-macros" -version = "0.19.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6115af052c7914c0cbb97195e5c72cb61c511527250074f5c041d1048b0d8b16" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -1468,7 +1448,6 @@ dependencies = [ name = "litebox_common_linux" version = "0.1.0" dependencies = [ - "bitfield", "bitflags 2.11.0", "cfg-if", "elf", diff --git a/litebox/src/platform/arch.rs b/litebox/src/platform/arch.rs new file mode 100644 index 000000000..99117f2b5 --- /dev/null +++ b/litebox/src/platform/arch.rs @@ -0,0 +1,48 @@ +// 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 context + fn get_arch_specific_register( + &self, + reg: &ArchSpecificRegister, + ) -> Result; + + /// Set the architecture-specific `reg` to `val`, for the current context + 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, +} diff --git a/litebox/src/platform/mock.rs b/litebox/src/platform/mock.rs index 4bcb936eb..5eed9f861 100644 --- a/litebox/src/platform/mock.rs +++ b/litebox/src/platform/mock.rs @@ -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: as PunchthroughToken>::Punchthrough, - ) -> Option> { - None + _reg: &ArchSpecificRegister, + ) -> Result { + Err(ArchSpecificError::RegisterUnsupported) + } + fn set_arch_specific_register( + &self, + _reg: &ArchSpecificRegister, + _val: usize, + ) -> Result<(), ArchSpecificError> { + Err(ArchSpecificError::RegisterUnsupported) } } diff --git a/litebox/src/platform/mod.rs b/litebox/src/platform/mod.rs index 2a0b6a9df..e5b347b28 100644 --- a/litebox/src/platform/mod.rs +++ b/litebox/src/platform/mod.rs @@ -7,6 +7,7 @@ //! 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; @@ -14,10 +15,10 @@ 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. @@ -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 { } @@ -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: as PunchthroughToken>::Punchthrough, - ) -> Option>; -} - -/// 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< - ::ReturnSuccess, - PunchthroughError<::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 { - #[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 { - #[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 PunchthroughToken for Either -where - L: PunchthroughToken, - R: PunchthroughToken, -{ - type Punchthrough = Either; - - fn execute( - self, - ) -> Result< - ::ReturnSuccess, - PunchthroughError<::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 Punchthrough for Either -where - L: Punchthrough, - R: Punchthrough, -{ - type ReturnSuccess = Either; - type ReturnFailure = EitherError; -} - /// A provider of raw mutexes pub trait RawMutexProvider { type RawMutex: RawMutex; @@ -669,15 +550,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. diff --git a/litebox/src/platform/trivial_providers.rs b/litebox/src/platform/trivial_providers.rs index a6293c246..bda4d01eb 100644 --- a/litebox/src/platform/trivial_providers.rs +++ b/litebox/src/platform/trivial_providers.rs @@ -6,10 +6,7 @@ //! Most users of LiteBox may possibly need more featureful providers, provided by other crates; //! however, some users might find these sufficient for their use case. -use super::{ - Punchthrough, PunchthroughError, PunchthroughProvider, PunchthroughToken, RawConstPointer, - RawMutPointer, TimerHandle, -}; +use super::{RawConstPointer, RawMutPointer, TimerHandle}; use zerocopy::{FromBytes, IntoBytes}; @@ -22,99 +19,6 @@ impl TimerHandle for UnsupportedTimerHandle { } } -/// A trivial provider, useful when no punchthrough is necessary. -pub struct ImpossiblePunchthroughProvider {} -impl PunchthroughProvider for ImpossiblePunchthroughProvider { - type PunchthroughToken<'a> = ImpossiblePunchthroughToken; - fn get_punchthrough_token_for<'a>( - &self, - _punchthrough: as PunchthroughToken>::Punchthrough, - ) -> Option> { - // Since `ImpossiblePunchthrough` is an empty enum, it is impossible to actually invoke - // `execute` upon it, meaning that the implementation here is irrelevant, since anything - // within it is provably unreachable. - unreachable!() - } -} -/// A [`Punchthrough`] for [`ImpossiblePunchthroughProvider`] -pub enum ImpossiblePunchthrough {} -impl Punchthrough for ImpossiblePunchthrough { - // Infallible has the same role as the never type (`!`) which will _eventually_ be stabilized in - // Rust. Since `Infallible` has no variant, a value of this type can never actually exist. - type ReturnSuccess = core::convert::Infallible; - type ReturnFailure = core::convert::Infallible; -} -/// A [`PunchthroughToken`] for [`ImpossiblePunchthrough`] -pub enum ImpossiblePunchthroughToken {} -impl PunchthroughToken for ImpossiblePunchthroughToken { - type Punchthrough = ImpossiblePunchthrough; - fn execute( - self, - ) -> Result< - ::ReturnSuccess, - PunchthroughError<::ReturnFailure>, - > { - // Since `ImpossiblePunchthroughToken` is an empty enum, it is impossible to actually invoke - // `execute` upon it, meaning that the implementation here is irrelevant, since anything - // within it is provably unreachable. - unreachable!() - } -} - -/// A trivial provider, useful when punchthroughs are be necessary, but might prefer to be -/// simply caught as "unimplemented" temporarily, while more infrastructure is set up. -pub struct IgnoredPunchthroughProvider {} -impl PunchthroughProvider for IgnoredPunchthroughProvider { - type PunchthroughToken<'a> = IgnoredPunchthroughToken; - fn get_punchthrough_token_for<'a>( - &self, - punchthrough: as PunchthroughToken>::Punchthrough, - ) -> Option> { - Some(IgnoredPunchthroughToken { punchthrough }) - } -} -/// A [`Punchthrough`] for [`IgnoredPunchthroughProvider`] -pub struct IgnoredPunchthrough { - data: &'static str, -} -impl Punchthrough for IgnoredPunchthrough { - type ReturnSuccess = Underspecified; - type ReturnFailure = Underspecified; -} -/// A [`PunchthroughToken`] for [`IgnoredPunchthrough`] -pub struct IgnoredPunchthroughToken { - punchthrough: IgnoredPunchthrough, -} -impl PunchthroughToken for IgnoredPunchthroughToken { - type Punchthrough = IgnoredPunchthrough; - fn execute( - self, - ) -> Result< - ::ReturnSuccess, - PunchthroughError<::ReturnFailure>, - > { - Err(PunchthroughError::Unimplemented(self.punchthrough.data)) - } -} - -/// An under-specified type that cannot be "inspected" or created; used for [`IgnoredPunchthrough`] -#[doc(hidden)] -pub struct Underspecified { - // Explicitly private field, to prevent destructuring or creation outside this module. - __private: (), -} -impl core::fmt::Debug for Underspecified { - fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - unreachable!("Underspecified is never constructed") - } -} -impl core::fmt::Display for Underspecified { - fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - unreachable!("Underspecified is never constructed") - } -} -impl core::error::Error for Underspecified {} - /// A trivial [`RawConstPointer`] that is literally just `*const T`. /// /// Useful for purely-userland contexts. diff --git a/litebox_common_linux/Cargo.toml b/litebox_common_linux/Cargo.toml index 9f1074faa..b2061b8f5 100644 --- a/litebox_common_linux/Cargo.toml +++ b/litebox_common_linux/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] -bitfield = "0.19.1" bitflags = "2.9.0" cfg-if = "1.0.0" elf = { version = "0.8.0", default-features = false } diff --git a/litebox_common_linux/src/errno/mod.rs b/litebox_common_linux/src/errno/mod.rs index 0aa23aae2..dafb0e5e0 100644 --- a/litebox_common_linux/src/errno/mod.rs +++ b/litebox_common_linux/src/errno/mod.rs @@ -579,3 +579,19 @@ impl From for Errno { } } } + +#[cfg(target_arch = "x86_64")] +impl From for Errno { + fn from(value: litebox::platform::ArchSpecificError) -> Self { + match value { + litebox::platform::ArchSpecificError::RegisterUnsupported => { + // Reaching here means that a shim is attempting to easily run on a platform that + // fully disallows it. There is no reasonable handling here, so we panic. + // XXX: should this be ENOSYS? + panic!() + } + litebox::platform::ArchSpecificError::RegisterReserved => Errno::EINVAL, + _ => unimplemented!(), + } + } +} diff --git a/litebox_common_linux/src/lib.rs b/litebox_common_linux/src/lib.rs index 578bd51a9..a7c9248cd 100644 --- a/litebox_common_linux/src/lib.rs +++ b/litebox_common_linux/src/lib.rs @@ -935,38 +935,6 @@ pub unsafe fn wrgsbase(gs_base: usize) { } } -/// Linux's `user_desc` struct used by the `set_thread_area` syscall. -#[repr(C, packed)] -#[derive(Debug, Clone, FromBytes, IntoBytes)] -pub struct UserDesc { - pub entry_number: u32, - pub base_addr: u32, - pub limit: u32, - pub flags: UserDescFlags, -} - -bitfield::bitfield! { - /// Flags for the `user_desc` struct. - #[derive(Clone, Copy, FromBytes, IntoBytes)] - #[repr(transparent)] - pub struct UserDescFlags(u32); - impl Debug; - /// 1 if the segment is 32-bit - pub seg_32bit, set_seg_32bit: 0; - /// Contents of the segment - pub contents, set_contents: 1, 2; - /// Read-exec only - pub read_exec_only, set_read_exec_only: 3; - /// Limit in pages - pub limit_in_pages, set_limit_in_pages: 4; - /// Segment not present - pub seg_not_present, set_seg_not_present: 5; - /// Usable by userland - pub useable, set_useable: 6; - /// 1 if the segment is 64-bit (x86_64 only) - pub lm, set_lm: 7; -} - /// Flags for the clone3 system call as defined in `/usr/include/linux/sched.h`. #[derive(Clone, Copy, Debug, FromBytes, IntoBytes)] #[repr(transparent)] @@ -2039,9 +2007,9 @@ pub enum SyscallRequest { args: Platform::RawConstPointer, }, /// Manipulate thread-local storage information. - /// Returns `ENOSYS` on 64-bit. + /// Returns `ENOSYS` on x86_64. SetThreadArea { - user_desc: Platform::RawMutPointer, + user_desc: Platform::RawMutPointer, }, ClockGettime { clockid: i32, @@ -2654,30 +2622,6 @@ impl SyscallRequest { } } -/// A set of syscalls that are allowed to be punched through to platforms that work with the Linux -/// shim. -/// -/// NOTE: It is assumed that all punchthroughs here are non-blocking. -#[derive(Debug)] -pub enum PunchthroughSyscall<'a, Platform: litebox::platform::RawPointerProvider> { - /// Set the FS base register to the value in `addr`. - #[cfg(target_arch = "x86_64")] - SetFsBase { addr: usize }, - /// Return the current value of the FS base register. - #[cfg(target_arch = "x86_64")] - GetFsBase, - /// An uninhabited variant to ensure the generics are referenced on all - /// architectures. Provider implementations won't need to match on this - /// variant, since Rust can see that it is uninhabited. - #[doc(hidden)] - _Phantom( - core::marker::PhantomData<&'a mut ()>, - core::marker::PhantomData Platform>, - // Make this variant uninhabited. - core::convert::Infallible, - ), -} - #[derive(Debug)] pub enum TimeParam { None, @@ -2763,13 +2707,6 @@ impl TimeParam { } } -impl litebox::platform::Punchthrough - for PunchthroughSyscall<'_, Platform> -{ - type ReturnSuccess = usize; - type ReturnFailure = errno::Errno; -} - /// Context saved when entering the kernel /// /// pt_regs from [Linux](https://elixir.bootlin.com/linux/v5.19.17/source/arch/x86/include/asm/ptrace.h#L59) diff --git a/litebox_platform_linux_kernel/src/lib.rs b/litebox_platform_linux_kernel/src/lib.rs index cc207d3da..415bd96f0 100644 --- a/litebox_platform_linux_kernel/src/lib.rs +++ b/litebox_platform_linux_kernel/src/lib.rs @@ -13,10 +13,10 @@ use litebox::mm::linux::PageRange; use litebox::platform::RawPointerProvider; use litebox::platform::page_mgmt::FixedAddressBehavior; use litebox::platform::{ - IPInterfaceProvider, ImmediatelyWokenUp, PageManagementProvider, Provider, Punchthrough, - PunchthroughProvider, PunchthroughToken, RawMutexProvider, TimeProvider, UnblockedOrTimedOut, + ArchSpecificError, ArchSpecificProvider, ArchSpecificRegister, IPInterfaceProvider, + ImmediatelyWokenUp, PageManagementProvider, Provider, RawMutexProvider, TimeProvider, + UnblockedOrTimedOut, }; -use litebox_common_linux::PunchthroughSyscall; use litebox_common_linux::errno::Errno; extern crate alloc; @@ -54,34 +54,6 @@ impl core::fmt::Debug for LinuxKernel { } } -pub struct LinuxPunchthroughToken<'a, Host: HostInterface> { - punchthrough: PunchthroughSyscall<'a, LinuxKernel>, - host: core::marker::PhantomData, -} - -impl<'a, Host: HostInterface> PunchthroughToken for LinuxPunchthroughToken<'a, Host> { - type Punchthrough = PunchthroughSyscall<'a, LinuxKernel>; - - fn execute( - self, - ) -> Result< - ::ReturnSuccess, - litebox::platform::PunchthroughError<::ReturnFailure>, - > { - let r = match self.punchthrough { - PunchthroughSyscall::SetFsBase { addr } => { - unsafe { litebox_common_linux::wrfsbase(addr) }; - Ok(0) - } - PunchthroughSyscall::GetFsBase => Ok(unsafe { litebox_common_linux::rdfsbase() }), - }; - match r { - Ok(v) => Ok(v), - Err(e) => Err(litebox::platform::PunchthroughError::Failure(e)), - } - } -} - impl Provider for LinuxKernel {} // TODO: implement pointer validation to ensure the pointers are in user space. @@ -99,17 +71,34 @@ impl RawPointerProvider for LinuxKernel { type RawMutPointer = UserMutPtr; } -impl PunchthroughProvider for LinuxKernel { - type PunchthroughToken<'a> = LinuxPunchthroughToken<'a, Host>; +impl ArchSpecificProvider for LinuxKernel { + fn set_arch_specific_register( + &self, + reg: &ArchSpecificRegister, + val: usize, + ) -> Result<(), ArchSpecificError> { + match reg { + ArchSpecificRegister::FsBase => { + unsafe { litebox_common_linux::wrfsbase(val) }; + Ok(()) + } + ArchSpecificRegister::GsBase => { + unsafe { litebox_common_linux::wrgsbase(val) }; + Ok(()) + } + _ => Err(ArchSpecificError::RegisterUnsupported), + } + } - fn get_punchthrough_token_for<'a>( + fn get_arch_specific_register( &self, - punchthrough: as PunchthroughToken>::Punchthrough, - ) -> Option> { - Some(LinuxPunchthroughToken { - punchthrough, - host: core::marker::PhantomData, - }) + reg: &ArchSpecificRegister, + ) -> Result { + match reg { + ArchSpecificRegister::FsBase => Ok(unsafe { litebox_common_linux::rdfsbase() }), + ArchSpecificRegister::GsBase => Ok(unsafe { litebox_common_linux::rdgsbase() }), + _ => Err(ArchSpecificError::RegisterUnsupported), + } } } diff --git a/litebox_platform_linux_userland/src/lib.rs b/litebox_platform_linux_userland/src/lib.rs index a1ccf7fdc..3c17df109 100644 --- a/litebox_platform_linux_userland/src/lib.rs +++ b/litebox_platform_linux_userland/src/lib.rs @@ -22,9 +22,7 @@ use litebox::platform::page_mgmt::{ use litebox::platform::{ImmediatelyWokenUp, RawConstPointer as _}; use litebox::shim::ContinueOperation; use litebox::utils::{ReinterpretSignedExt, ReinterpretUnsignedExt as _, TruncateExt}; -use litebox_common_linux::{ - MRemapFlags, MapFlags, ProtFlags, PunchthroughSyscall, vmap::VmapManager, -}; +use litebox_common_linux::{MRemapFlags, MapFlags, ProtFlags, vmap::VmapManager}; use zerocopy::{FromBytes, IntoBytes}; @@ -1193,40 +1191,44 @@ impl litebox::platform::SystemTime for SystemTime { } } -pub struct PunchthroughToken<'a> { - punchthrough: PunchthroughSyscall<'a, LinuxUserland>, -} - -impl<'a> litebox::platform::PunchthroughToken for PunchthroughToken<'a> { - type Punchthrough = PunchthroughSyscall<'a, LinuxUserland>; - fn execute( - self, - ) -> Result< - ::ReturnSuccess, - litebox::platform::PunchthroughError< - ::ReturnFailure, - >, - > { - match self.punchthrough { - // We swap gs and fs before and after a syscall so at this point guest's fs base is stored in gs - #[cfg(target_arch = "x86_64")] - PunchthroughSyscall::SetFsBase { addr } => { - set_guest_fsbase(addr); - Ok(0) +#[cfg(target_arch = "x86_64")] +impl litebox::platform::ArchSpecificProvider for LinuxUserland { + // We swap gs and fs before and after a syscall, so while handling a guest + // syscall the guest's fs base is stored in the gs base register; the + // per-thread `guest_fsbase` slot holds the value that will be programmed + // into fs base on guest re-entry. + fn set_arch_specific_register( + &self, + reg: &litebox::platform::ArchSpecificRegister, + val: usize, + ) -> Result<(), litebox::platform::ArchSpecificError> { + match reg { + litebox::platform::ArchSpecificRegister::FsBase => { + set_guest_fsbase(val); + Ok(()) } - #[cfg(target_arch = "x86_64")] - PunchthroughSyscall::GetFsBase => Ok(get_guest_fsbase()), + litebox::platform::ArchSpecificRegister::GsBase => { + // GS base is used internally by this platform to hold the host + // TLS base across the guest/host fs-gs swap, so it is not + // directly programmable by the guest. + Err(litebox::platform::ArchSpecificError::RegisterReserved) + } + _ => Err(litebox::platform::ArchSpecificError::RegisterUnsupported), } } -} - -impl litebox::platform::PunchthroughProvider for LinuxUserland { - type PunchthroughToken<'a> = PunchthroughToken<'a>; - fn get_punchthrough_token_for<'a>( + fn get_arch_specific_register( &self, - punchthrough: as litebox::platform::PunchthroughToken>::Punchthrough, - ) -> Option> { - Some(PunchthroughToken { punchthrough }) + reg: &litebox::platform::ArchSpecificRegister, + ) -> Result { + match reg { + litebox::platform::ArchSpecificRegister::FsBase => Ok(get_guest_fsbase()), + litebox::platform::ArchSpecificRegister::GsBase => { + // See note above: gs base is reserved for host TLS on this + // platform and is not exposed to the guest. + Err(litebox::platform::ArchSpecificError::RegisterReserved) + } + _ => Err(litebox::platform::ArchSpecificError::RegisterUnsupported), + } } } @@ -1694,11 +1696,6 @@ unsafe impl litebox::platform::ThreadLocalStorageProvider for LinuxUserland { unsafe fn replace_thread_local_storage(value: *mut ()) -> *mut () { PLATFORM_TLS.replace(value) } - - #[cfg(target_arch = "x86_64")] - fn clear_guest_thread_local_storage() { - set_guest_fsbase(0); - } } static mut NEXT_SA: [libc::sigaction; 64] = unsafe { core::mem::zeroed() }; diff --git a/litebox_platform_lvbs/src/lib.rs b/litebox_platform_lvbs/src/lib.rs index 56914cd75..fe36ce202 100644 --- a/litebox_platform_lvbs/src/lib.rs +++ b/litebox_platform_lvbs/src/lib.rs @@ -13,9 +13,10 @@ use core::{ }; use hashbrown::HashMap; use litebox::platform::{ - IPInterfaceProvider, ImmediatelyWokenUp, PageManagementProvider, Punchthrough, - PunchthroughProvider, PunchthroughToken, RawMutex as _, RawMutexProvider, RawPointerProvider, - StdioProvider, TimeProvider, UnblockedOrTimedOut, page_mgmt::DeallocationError, + ArchSpecificError, ArchSpecificProvider, ArchSpecificRegister, IPInterfaceProvider, + ImmediatelyWokenUp, PageManagementProvider, RawMutex as _, RawMutexProvider, + RawPointerProvider, StdioProvider, TimeProvider, UnblockedOrTimedOut, + page_mgmt::DeallocationError, }; use litebox::{ mm::linux::{PAGE_SIZE, PageRange}, @@ -23,12 +24,12 @@ use litebox::{ shim::ContinueOperation, utils::TruncateExt, }; +use litebox_common_linux::errno::Errno; #[cfg(feature = "optee_syscall")] use litebox_common_linux::vmap::{ PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, VmapManager, }; -use litebox_common_linux::{PunchthroughSyscall, errno::Errno}; use x86_64::{ VirtAddr, structures::paging::{ @@ -395,11 +396,6 @@ pub struct LinuxKernel { vtl0_kernel_info: Vtl0KernelInfo, } -pub struct LinuxPunchthroughToken<'a, Host: HostInterface> { - punchthrough: PunchthroughSyscall<'a, LinuxKernel>, - host: core::marker::PhantomData, -} - /// [`litebox::platform::common_providers::userspace_pointers::ValidateAccess`] /// implementation for LVBS that provides SMAP support. pub struct LvbsValidateAccess; @@ -464,40 +460,34 @@ impl RawPointerProvider for LinuxKernel { type RawMutPointer = UserMutPtr; } -impl<'a, Host: HostInterface> PunchthroughToken for LinuxPunchthroughToken<'a, Host> { - type Punchthrough = PunchthroughSyscall<'a, LinuxKernel>; - - fn execute( - self, - ) -> Result< - ::ReturnSuccess, - litebox::platform::PunchthroughError<::ReturnFailure>, - > { - let r = match self.punchthrough { - PunchthroughSyscall::SetFsBase { addr } => { - unsafe { litebox_common_linux::wrfsbase(addr) }; - Ok(0) +impl ArchSpecificProvider for LinuxKernel { + fn set_arch_specific_register( + &self, + reg: &ArchSpecificRegister, + val: usize, + ) -> Result<(), ArchSpecificError> { + match reg { + ArchSpecificRegister::FsBase => { + unsafe { litebox_common_linux::wrfsbase(val) }; + Ok(()) } - PunchthroughSyscall::GetFsBase => Ok(unsafe { litebox_common_linux::rdfsbase() }), - }; - match r { - Ok(v) => Ok(v), - Err(e) => Err(litebox::platform::PunchthroughError::Failure(e)), + ArchSpecificRegister::GsBase => { + unsafe { litebox_common_linux::wrgsbase(val) }; + Ok(()) + } + _ => Err(ArchSpecificError::RegisterUnsupported), } } -} - -impl PunchthroughProvider for LinuxKernel { - type PunchthroughToken<'a> = LinuxPunchthroughToken<'a, Host>; - fn get_punchthrough_token_for<'a>( + fn get_arch_specific_register( &self, - punchthrough: as PunchthroughToken>::Punchthrough, - ) -> Option> { - Some(LinuxPunchthroughToken { - punchthrough, - host: core::marker::PhantomData, - }) + reg: &ArchSpecificRegister, + ) -> Result { + match reg { + ArchSpecificRegister::FsBase => Ok(unsafe { litebox_common_linux::rdfsbase() }), + ArchSpecificRegister::GsBase => Ok(unsafe { litebox_common_linux::rdgsbase() }), + _ => Err(ArchSpecificError::RegisterUnsupported), + } } } diff --git a/litebox_platform_windows_userland/src/lib.rs b/litebox_platform_windows_userland/src/lib.rs index 396282544..2652269ab 100644 --- a/litebox_platform_windows_userland/src/lib.rs +++ b/litebox_platform_windows_userland/src/lib.rs @@ -23,7 +23,6 @@ use litebox::platform::page_mgmt::{ }; use litebox::shim::{ContinueOperation, Exception}; use litebox::utils::TruncateExt as _; -use litebox_common_linux::PunchthroughSyscall; use windows_sys::Win32::Foundation::{self as Win32_Foundation, FILETIME}; use windows_sys::Win32::{ @@ -1473,41 +1472,40 @@ impl litebox::platform::SystemTime for SystemTime { } } -pub struct PunchthroughToken<'a> { - punchthrough: PunchthroughSyscall<'a, WindowsUserland>, -} - -impl<'a> litebox::platform::PunchthroughToken for PunchthroughToken<'a> { - type Punchthrough = PunchthroughSyscall<'a, WindowsUserland>; - fn execute( - self, - ) -> Result< - ::ReturnSuccess, - litebox::platform::PunchthroughError< - ::ReturnFailure, - >, - > { - match self.punchthrough { - PunchthroughSyscall::SetFsBase { addr } => { +impl litebox::platform::ArchSpecificProvider for WindowsUserland { + fn set_arch_specific_register( + &self, + reg: &litebox::platform::ArchSpecificRegister, + val: usize, + ) -> Result<(), litebox::platform::ArchSpecificError> { + match reg { + litebox::platform::ArchSpecificRegister::FsBase => { // Use WindowsUserland's per-thread FS base management system - WindowsUserland::set_thread_fs_base(addr); - Ok(0) + Self::set_thread_fs_base(val); + Ok(()) } - PunchthroughSyscall::GetFsBase => { - // Use the stored FS base value from our per-thread storage - Ok(WindowsUserland::get_thread_fs_base()) + litebox::platform::ArchSpecificRegister::GsBase => { + // Windows uses GS for its own thread environment block + // (TEB); the host platform does not expose a safe way for + // the guest to program gs base without breaking the host. + Err(litebox::platform::ArchSpecificError::RegisterReserved) } + _ => Err(litebox::platform::ArchSpecificError::RegisterUnsupported), } } -} -impl litebox::platform::PunchthroughProvider for WindowsUserland { - type PunchthroughToken<'a> = PunchthroughToken<'a>; - fn get_punchthrough_token_for<'a>( + fn get_arch_specific_register( &self, - punchthrough: as litebox::platform::PunchthroughToken>::Punchthrough, - ) -> Option> { - Some(PunchthroughToken { punchthrough }) + reg: &litebox::platform::ArchSpecificRegister, + ) -> Result { + match reg { + litebox::platform::ArchSpecificRegister::FsBase => Ok(Self::get_thread_fs_base()), + litebox::platform::ArchSpecificRegister::GsBase => { + // See note above: gs base is reserved by the Windows host. + Err(litebox::platform::ArchSpecificError::RegisterReserved) + } + _ => Err(litebox::platform::ArchSpecificError::RegisterUnsupported), + } } } @@ -2052,10 +2050,6 @@ unsafe impl litebox::platform::ThreadLocalStorageProvider for WindowsUserland { unsafe fn replace_thread_local_storage(new_tls: *mut ()) -> *mut () { PLATFORM_TLS.replace(new_tls) } - - fn clear_guest_thread_local_storage() { - Self::init_thread_fs_base(); - } } impl litebox::platform::CrngProvider for WindowsUserland { diff --git a/litebox_shim_linux/src/syscalls/process.rs b/litebox_shim_linux/src/syscalls/process.rs index 15e7c6f88..1da295756 100644 --- a/litebox_shim_linux/src/syscalls/process.rs +++ b/litebox_shim_linux/src/syscalls/process.rs @@ -16,11 +16,10 @@ use core::time::Duration; use litebox::event::wait::WaitError; use litebox::mm::linux::VmFlags; use litebox::platform::ThreadProvider; -use litebox::platform::{Instant as _, SystemTime as _, TimeProvider}; use litebox::platform::{ - PunchthroughProvider as _, PunchthroughToken as _, RawConstPointer as _, RawMutex as _, - ThreadLocalStorageProvider as _, + ArchSpecificProvider as _, ArchSpecificRegister, RawConstPointer as _, RawMutex as _, }; +use litebox::platform::{Instant as _, SystemTime as _, TimeProvider}; use litebox::platform::{RawMutPointer as _, TimerHandle, TimerProvider}; use litebox::sync::Mutex; use litebox::utils::TruncateExt as _; @@ -395,30 +394,17 @@ impl Task { ) -> Result<(), Errno> { match arg { #[cfg(target_arch = "x86_64")] - ArchPrctlArg::SetFs(addr) => { - let punchthrough = litebox_common_linux::PunchthroughSyscall::SetFsBase { addr }; - let token = self - .global - .platform - .get_punchthrough_token_for(punchthrough) - .expect("Failed to get punchthrough token for SET_FS"); - token.execute().map(|_| ()).map_err(|e| match e { - litebox::platform::PunchthroughError::Failure(errno) => errno, - _ => unimplemented!("Unsupported punchthrough error {:?}", e), - }) - } + ArchPrctlArg::SetFs(addr) => self + .global + .platform + .set_arch_specific_register(&ArchSpecificRegister::FsBase, addr) + .map_err(Errno::from), #[cfg(target_arch = "x86_64")] ArchPrctlArg::GetFs(addr) => { - let punchthrough = litebox_common_linux::PunchthroughSyscall::GetFsBase; - let token = self + let fsbase = self .global .platform - .get_punchthrough_token_for(punchthrough) - .expect("Failed to get punchthrough token for GET_FS"); - let fsbase = token.execute().map_err(|e| match e { - litebox::platform::PunchthroughError::Failure(errno) => errno, - _ => unimplemented!("Unsupported punchthrough error {:?}", e), - })?; + .get_arch_specific_register(&ArchSpecificRegister::FsBase)?; addr.write_at_offset(0, fsbase).ok_or(Errno::EFAULT)?; Ok(()) } @@ -1389,7 +1375,10 @@ impl Task { unsafe { self.global.pm.release_memory(release) } .expect("failed to release memory mappings"); - litebox_platform_multiplex::Platform::clear_guest_thread_local_storage(); + self.global + .platform + .set_arch_specific_register(&ArchSpecificRegister::FsBase, 0) + .expect("failed to clear guest TLS on execve"); self.load_program(loader, argv_vec, envp_vec) .expect("TODO: terminate the process cleanly"); diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index de6207fd3..023aa2ecc 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -17,7 +17,7 @@ use hashbrown::HashMap; use litebox::{ LiteBox, mm::{PageManager, linux::PAGE_SIZE}, - platform::{PunchthroughProvider, PunchthroughToken, RawConstPointer as _, RawMutPointer as _}, + platform::{RawConstPointer as _, RawMutPointer as _}, shim::ContinueOperation, utils::{ReinterpretUnsignedExt, TruncateExt}, }; @@ -798,18 +798,14 @@ impl Task { /// every TA entry. #[cfg(target_arch = "x86_64")] fn restore_guest_tls(&self) { + use litebox::platform::ArchSpecificProvider as _; let addr = self.tls_base_addr.get(); if addr == 0 { return; // TLS not allocated yet } - let punchthrough = litebox_common_linux::PunchthroughSyscall::SetFsBase { addr }; - let token = litebox_platform_multiplex::platform() - .get_punchthrough_token_for(punchthrough) - .expect("Failed to get punchthrough token for SET_FS"); - let _ = token.execute().map(|_| ()).map_err(|e| match e { - litebox::platform::PunchthroughError::Failure(errno) => errno, - _ => unimplemented!("Unsupported punchthrough error {:?}", e), - }); + litebox_platform_multiplex::platform() + .set_arch_specific_register(&litebox::platform::ArchSpecificRegister::FsBase, addr) + .expect("requires guaranteed platform support for FsBase"); } /// Retrieve the result of the `ldelf` execution.