Skip to content
Merged
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
2 changes: 1 addition & 1 deletion dev_tests/src/ratchet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn ratchet_globals() -> Result<()> {
("litebox/", 9),
("litebox_platform_linux_kernel/", 6),
("litebox_platform_linux_userland/", 5),
("litebox_platform_lvbs/", 23),
("litebox_platform_lvbs/", 24),
("litebox_platform_multiplex/", 1),
("litebox_platform_windows_userland/", 8),
("litebox_runner_lvbs/", 5),
Expand Down
19 changes: 17 additions & 2 deletions litebox_common_linux/src/vmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ pub unsafe trait VmapManager<const ALIGN: usize> {
Err(PhysPointerError::UnsupportedOperation)
}

/// Map pages while bypassing platform-defined ordinary access checks. Ordinary [`Self::vmap`]
/// may delegate here after performing those checks. The default is deny.
///
/// # Safety
///
/// In addition to [`Self::vmap`]'s raw-mapping requirements, the caller must independently
/// authorize bypassing the omitted platform checks.
unsafe fn vmap_privileged(
&self,
_pages: &PhysPageAddrArray<ALIGN>,
_perms: PhysPageMapPermissions,
) -> Result<Self::MapInfo, PhysPointerError> {
Err(PhysPointerError::UnsupportedOperation)
}

/// Unmap the previously mapped virtually contiguous addresses ([`Self::MapInfo`]).
///
/// This function is analogous to Linux kernel's `vunmap()`.
Expand Down Expand Up @@ -78,7 +93,7 @@ pub unsafe trait VmapManager<const ALIGN: usize> {
/// platform-defined foreign-memory VA ranges, never through LiteBox-owned VA ranges.
fn validate_unowned(&self, pages: &PhysPageAddrArray<ALIGN>) -> Result<(), PhysPointerError>;

/// Protect the given physical pages to ensure concurrent read or exclusive write access:
/// Protect the given physical pages according to `perms`:
/// - Read protection: prevent others from writing to the pages.
/// - Read/write protection: prevent others from reading or writing to the pages.
/// - No protection: allow others to read and write the pages.
Expand All @@ -92,7 +107,7 @@ pub unsafe trait VmapManager<const ALIGN: usize> {
///
/// This function relies on hypercalls or other privileged hardware features and assumes those features
/// are safe to use.
/// The caller should unprotect the pages when they are no longer needed to access them.
/// Callers should restore ordinary access when protection is no longer needed.
unsafe fn protect(
&self,
pages: &PhysPageAddrArray<ALIGN>,
Expand Down
37 changes: 29 additions & 8 deletions litebox_platform_lvbs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,21 @@ pub mod mshv;

pub mod syscall_entry;

/// Mapping info returned by [`LinuxKernel`]'s [`VmapManager::vmap`].
/// Mapping metadata. Ordinary writable mappings retain an opaque protected-frame access guard for
/// the mapping's lifetime.
pub struct LvbsPhysPageMapInfo {
base: *mut u8,
size: usize,
protected_frame_access: Option<crate::mshv::vsm::ProtectedFrameAccessGuard<'static>>,
}

impl LvbsPhysPageMapInfo {
fn new(base: *mut u8, size: usize) -> Self {
Self { base, size }
Self {
base,
size,
protected_frame_access: None,
}
}
}

Expand Down Expand Up @@ -454,11 +460,6 @@ impl<const ALIGN: usize> GlobalVmapManager<ALIGN> for Vmap {
}
}

pub type Vtl0PhysConstPtr<T, const ALIGN: usize> =
litebox_common_linux::physical_pointers::PhysConstPtr<T, ALIGN, Vmap>;
pub type Vtl0PhysMutPtr<T, const ALIGN: usize> =
litebox_common_linux::physical_pointers::PhysMutPtr<T, ALIGN, Vmap>;

impl<Host: HostInterface> RawPointerProvider for LinuxKernel<Host> {
type RawConstPointer<T: FromBytes> = UserConstPtr<T>;
type RawMutPointer<T: FromBytes + IntoBytes> = UserMutPtr<T>;
Expand Down Expand Up @@ -1128,6 +1129,26 @@ unsafe impl<Host: HostInterface, const ALIGN: usize> VmapManager<ALIGN> for Linu
&self,
pages: &PhysPageAddrArray<ALIGN>,
perms: PhysPageMapPermissions,
) -> Result<Self::MapInfo, PhysPointerError> {
let protected_frame_access = if perms.contains(PhysPageMapPermissions::WRITE) {
// This shared guard spans map/copy/unmap. It permits concurrent foreign-memory writes
// but does not support re-entry into a VTL protection change.
Some(crate::mshv::vsm::protected_frame_registry().acquire_access_guard(pages)?)
} else {
None
};
// SAFETY: ordinary writable mappings were checked against protected and in-flight frames;
// the guard is retained through map, access, and unmap. `vmap_privileged` provides the
// shared raw mapping implementation.
let mut map_info = unsafe { self.vmap_privileged(pages, perms)? };
map_info.protected_frame_access = protected_frame_access;
Ok(map_info)
}

unsafe fn vmap_privileged(
&self,
pages: &PhysPageAddrArray<ALIGN>,
perms: PhysPageMapPermissions,
) -> Result<Self::MapInfo, PhysPointerError> {
if pages.is_empty() {
return Err(PhysPointerError::InvalidPhysicalAddress(0));
Expand Down Expand Up @@ -1306,7 +1327,7 @@ unsafe impl<Host: HostInterface, const ALIGN: usize> VmapManager<ALIGN> for Linu
}

let mem_attr = if perms.contains(PhysPageMapPermissions::WRITE) {
// VTL1 wants to write data to the pages, preventing VTL0 from reading/executing the pages.
// VTL1 needs writable access, so deny VTL0 all access.
crate::mshv::heki::MemAttr::empty()
} else if perms.contains(PhysPageMapPermissions::READ) {
// VTL1 wants to read data from the pages, preventing VTL0 from writing to the pages.
Expand Down
4 changes: 4 additions & 0 deletions litebox_platform_lvbs/src/mshv/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ pub enum VsmError {
#[error("invalid module token")]
ModuleTokenInvalid,

#[error("physical frames overlap already-protected or reserved memory")]
ProtectedFrameOverlap,

// Kernel Symbol Table Errors
#[error("no kernel symbol table found")]
KernelSymbolTableNotFound,
Expand Down Expand Up @@ -212,6 +215,7 @@ impl From<VsmError> for Errno {
| VsmError::ModuleMemoryTypeInvalid
| VsmError::ModuleRelocationInvalid
| VsmError::ModuleTokenInvalid
| VsmError::ProtectedFrameOverlap
| VsmError::KexecTypeInvalid
| VsmError::KexecImageSegmentsInvalid
| VsmError::SymbolTableEmpty
Expand Down
65 changes: 65 additions & 0 deletions litebox_platform_lvbs/src/mshv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,71 @@ pub mod vsm_intercept;
pub mod vtl1_mem_layout;
pub mod vtl_switch;

use litebox_common_linux::vmap::{
GlobalVmapManager, PhysPageAddrArray, PhysPageMapPermissions, PhysPointerError, VmapManager,
};

/// Provider for MSHV operations authorized to modify protected VTL0 frames.
struct PrivilegedVmap;

impl<const ALIGN: usize> GlobalVmapManager<ALIGN> for PrivilegedVmap {
type Manager = PrivilegedVmap;

fn manager() -> &'static Self::Manager {
&PrivilegedVmap
}
}

unsafe impl<const ALIGN: usize> VmapManager<ALIGN> for PrivilegedVmap {
type MapInfo = crate::LvbsPhysPageMapInfo;

unsafe fn vmap(
&self,
pages: &PhysPageAddrArray<ALIGN>,
perms: PhysPageMapPermissions,
) -> Result<Self::MapInfo, PhysPointerError> {
// SAFETY: callers uphold the raw mapping contract. This provider is used only for
// independently authorized HEKI patch and ring-buffer writes.
unsafe { crate::platform_low().vmap_privileged(pages, perms) }
}

unsafe fn vunmap(
&self,
map_info: Self::MapInfo,
) -> Result<(), (PhysPointerError, Self::MapInfo)> {
// SAFETY: `map_info` came from the same LVBS mapper and has no outstanding uses beyond the
// physical-pointer guard that is dropping it.
unsafe {
<crate::host::LvbsLinuxKernel as VmapManager<ALIGN>>::vunmap(
crate::platform_low(),
map_info,
)
}
}

fn validate_unowned(&self, pages: &PhysPageAddrArray<ALIGN>) -> Result<(), PhysPointerError> {
crate::platform_low().validate_unowned(pages)
}

unsafe fn protect(
&self,
pages: &PhysPageAddrArray<ALIGN>,
perms: PhysPageMapPermissions,
) -> Result<(), PhysPointerError> {
// SAFETY: callers uphold `VmapManager::protect`; this forwards unchanged to LVBS.
unsafe { crate::platform_low().protect(pages, perms) }
}
}

type Vtl0PhysConstPtr<T, const ALIGN: usize> =
litebox_common_linux::physical_pointers::PhysConstPtr<T, ALIGN, crate::Vmap>;

/// Mutable VTL0 pointer reserved for validated HEKI text patching and the fixed-address log ring
/// buffer. It bypasses ordinary protected-frame access checks and synchronization. Do not use it for other
/// VTL0 destinations that could enable confused-deputy writes.
type PrivilegedVtl0PhysMutPtr<T, const ALIGN: usize> =
litebox_common_linux::physical_pointers::PhysMutPtr<T, ALIGN, PrivilegedVmap>;

use crate::arch::MAX_CORES;
use crate::mshv::vtl1_mem_layout::PAGE_SIZE;
use modular_bitfield::prelude::*;
Expand Down
13 changes: 8 additions & 5 deletions litebox_platform_lvbs/src/mshv/ringbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

//! RingBuffer implementation and functions

use crate::Vtl0PhysMutPtr;
use super::PrivilegedVtl0PhysMutPtr;
use core::fmt;
use litebox::mm::linux::PAGE_SIZE;
use litebox::utils::TruncateExt;
Expand Down Expand Up @@ -96,7 +96,7 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) ->
span.push(addr);
}

let Ok(ptr) = Vtl0PhysMutPtr::<u8, PAGE_SIZE>::new(&span, in_page_offset) else {
let Ok(ptr) = PrivilegedVtl0PhysMutPtr::<u8, PAGE_SIZE>::new(&span, in_page_offset) else {
return advance_offset(size, write_offset, buf.len());
};
let _ = ptr.write_slice_at_offset(0, buf);
Expand All @@ -108,9 +108,12 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) ->
/// after attempting the write.
fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize {
let write_slice = |pa: PhysAddr, slice: &[u8]| -> bool {
Vtl0PhysMutPtr::<u8, PAGE_SIZE>::with_contiguous_pages(pa.as_u64().trunc(), slice.len())
.and_then(|ptr| ptr.write_slice_at_offset(0, slice))
.is_ok()
PrivilegedVtl0PhysMutPtr::<u8, PAGE_SIZE>::with_contiguous_pages(
Comment thread
wdcui marked this conversation as resolved.
pa.as_u64().trunc(),
slice.len(),
)
.and_then(|ptr| ptr.write_slice_at_offset(0, slice))
.is_ok()
};

if buf.len() >= size {
Expand Down
Loading