Skip to content

Commit c58e8d6

Browse files
author
Sangho Lee
committed
harden VTL memory protection
1 parent 56174aa commit c58e8d6

3 files changed

Lines changed: 47 additions & 11 deletions

File tree

litebox_platform_lvbs/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,11 @@ impl<Host: HostInterface> LinuxKernel<Host> {
628628
CPU_MHZ.store(cpu_mhz, core::sync::atomic::Ordering::Relaxed);
629629
}
630630

631+
/// Returns the physical frame range belonging to VTL1.
632+
pub fn vtl1_phys_frame_range(&self) -> PhysFrameRange<Size4KiB> {
633+
self.vtl1_phys_frame_range
634+
}
635+
631636
/// This function maps VTL0 physical page frames containing the physical addresses
632637
/// from `phys_start` to `phys_end` to the VTL1 kernel page table. It internally page aligns
633638
/// the input addresses to ensure the mapped memory area covers the entire input addresses

litebox_platform_lvbs/src/mshv/error.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ pub enum VsmError {
4141
#[error("invalid physical address")]
4242
InvalidPhysicalAddress,
4343

44+
#[error("physical address range overlaps with VTL1 memory")]
45+
Vtl1MemoryOverlap,
46+
4447
// Memory/Data Errors
4548
#[error("invalid memory attributes")]
4649
MemoryAttributeInvalid,
@@ -172,7 +175,8 @@ impl From<VsmError> for Errno {
172175
| VsmError::BootSignalWriteFailed
173176
| VsmError::CpuOnlineMaskCopyFailed
174177
| VsmError::HekiPagesCopyFailed
175-
| VsmError::Vtl0CopyFailed => Errno::EFAULT,
178+
| VsmError::Vtl0CopyFailed
179+
| VsmError::Vtl1MemoryOverlap => Errno::EFAULT,
176180

177181
// Not found errors
178182
VsmError::SystemCertificatesNotFound

litebox_platform_lvbs/src/mshv/vsm.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,10 @@ pub(crate) fn init(is_bsp: bool) {
8585
if is_bsp {
8686
if let Ok((start, size)) = get_vtl1_memory_info() {
8787
debug_serial_println!("VSM: Protect GPAs from {:#x} to {:#x}", start, start + size);
88-
if protect_physical_memory_range(
89-
PhysFrame::range(
90-
PhysFrame::containing_address(PhysAddr::new(start)),
91-
PhysFrame::containing_address(PhysAddr::new(start + size)),
92-
),
93-
MemAttr::empty(),
94-
)
88+
if protect_vtl1_physical_memory_range(PhysFrame::range(
89+
PhysFrame::containing_address(PhysAddr::new(start)),
90+
PhysFrame::containing_address(PhysAddr::new(start + size)),
91+
))
9592
.is_err()
9693
{
9794
panic!("Failed to protect VTL1 memory");
@@ -1306,14 +1303,20 @@ fn copy_heki_pages_from_vtl0(pa: u64, nranges: u64) -> Option<Vec<HekiPage>> {
13061303
Some(heki_pages)
13071304
}
13081305

1309-
/// This function protects a physical memory range. It is a safe wrapper for `hv_modify_vtl_protection_mask`.
1310-
/// `phys_frame_range` specifies the physical frame range to protect
1311-
/// `mem_attr` specifies the memory attributes to be applied to the range
1306+
/// This function protects a VTL0 physical memory range from potentially compromised VTL0 by
1307+
/// restricting its access permissions using VTL protection mask (e.g., kernel code integrity).
1308+
/// It is a safe wrapper for `hv_modify_vtl_protection_mask`. `phys_frame_range` specifies the
1309+
/// physical frame range to protect (must belong to VTL0) `mem_attr` specifies the memory
1310+
/// attributes (VTL0's allowed access) to be applied to the range
13121311
#[inline]
13131312
pub(crate) fn protect_physical_memory_range(
13141313
phys_frame_range: PhysFrameRange<Size4KiB>,
13151314
mem_attr: MemAttr,
13161315
) -> Result<(), VsmError> {
1316+
let vtl1_range = crate::platform_low().vtl1_phys_frame_range();
1317+
if phys_frame_range.start < vtl1_range.end && vtl1_range.start < phys_frame_range.end {
1318+
return Err(VsmError::Vtl1MemoryOverlap);
1319+
}
13171320
let pa = phys_frame_range.start.start_address().as_u64();
13181321
let num_pages = phys_frame_range.count() as u64;
13191322
if num_pages > 0 {
@@ -1323,6 +1326,30 @@ pub(crate) fn protect_physical_memory_range(
13231326
Ok(())
13241327
}
13251328

1329+
/// This function is a variant of [`protect_physical_memory_range`] to protect a VTL1 physical memory range.
1330+
/// Unlike [`protect_physical_memory_range`], this is intended exclusively for securing VTL1's own pages.
1331+
/// VTL0 should never access VTL1 memory, so the memory attribute is always empty (no read, write, or execute).
1332+
///
1333+
/// Note. This function doesn't check whether `phys_frame_range` belongs to VTL1 because it is called by BSP
1334+
/// before the kernel platform data structure is initialized. To this end, one might call this function with
1335+
/// a VTL0 physical memory range which only restricts access to the range.
1336+
#[inline]
1337+
pub(crate) fn protect_vtl1_physical_memory_range(
1338+
phys_frame_range: PhysFrameRange<Size4KiB>,
1339+
) -> Result<(), VsmError> {
1340+
let pa = phys_frame_range.start.start_address().as_u64();
1341+
let num_pages = phys_frame_range.count() as u64;
1342+
if num_pages > 0 {
1343+
hv_modify_vtl_protection_mask(
1344+
pa,
1345+
num_pages,
1346+
mem_attr_to_hv_page_prot_flags(MemAttr::empty()),
1347+
)
1348+
.map_err(VsmError::HypercallFailed)?;
1349+
}
1350+
Ok(())
1351+
}
1352+
13261353
/// Data structure for maintaining the memory content of a kernel module by its sections. Currently, it only maintains
13271354
/// certain sections like `.text` and `.init.text` which are needed for module validation.
13281355
pub struct ModuleMemory {

0 commit comments

Comments
 (0)