Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for CVM's untrusted MMIO regions #60

Merged
merged 2 commits into from
May 30, 2024
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
657 changes: 657 additions & 0 deletions security-monitor/Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::mmio::MmioAccessFault;
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::handlers::virtual_instructions::VirtualInstruction;

/// Transformation of the confidential hart state in a response to processing of a confidential hart call.
pub enum ApplyToConfidentialHart {
MmioAccessFault(MmioAccessFault),
SbiResponse(SbiResponse),
VirtualInstruction(VirtualInstruction),
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl<'a> ConfidentialFlow<'a> {
/// execute the confidential hart on the hardware hart.
pub fn apply_and_exit_to_confidential_hart(mut self, transformation: ApplyToConfidentialHart) -> ! {
match transformation {
ApplyToConfidentialHart::MmioAccessFault(v) => v.apply_to_confidential_hart(self.confidential_hart_mut()),
ApplyToConfidentialHart::SbiResponse(v) => v.apply_to_confidential_hart(self.confidential_hart_mut()),
ApplyToConfidentialHart::VirtualInstruction(v) => v.apply_to_confidential_hart(self.confidential_hart_mut()),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::handlers::sbi::{SbiRequest, SbiResponse};
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::sbi::CovgExtension;
use crate::core::architecture::GeneralPurposeRegister;
use crate::core::control_data::ConfidentialHart;
use crate::core::control_data::{ConfidentialHart, ConfidentialVmMmioRegion, ControlData, PendingRequest};
use crate::non_confidential_flow::DeclassifyToHypervisor;

pub struct AddMmioRegion {
region_start_address: usize,
Expand All @@ -20,10 +22,20 @@ impl AddMmioRegion {
}

pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
// TODO: make sure region_start_address is aligned to 4KiB
// TODO: make sure the region_start_address is a valid guest address
// TODO: make sure this region does not overlap with any other previously defined region
let transformation = ApplyToConfidentialHart::SbiResponse(SbiResponse::success());
confidential_flow.apply_and_exit_to_confidential_hart(transformation)
match ControlData::try_confidential_vm(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
Ok(confidential_vm.add_mmio_region(ConfidentialVmMmioRegion::new(self.region_start_address, self.region_length)?)?)
}) {
Ok(_) => confidential_flow
.set_pending_request(PendingRequest::SbiRequest())
.into_non_confidential_flow()
.declassify_and_exit_to_hypervisor(DeclassifyToHypervisor::SbiRequest(self.sbi_add_mmio_region())),
Err(error) => {
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::SbiResponse(SbiResponse::error(error)))
}
}
}

fn sbi_add_mmio_region(&self) -> SbiRequest {
SbiRequest::new(CovgExtension::EXTID, CovgExtension::SBI_EXT_COVG_ADD_MMIO_REGION, self.region_start_address, self.region_length)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::core::control_data::{ConfidentialHart, ConfidentialVmId, ConfidentialVmMmioRegion, ControlData};
use core::mem;

pub struct MmioAccessFault {
cause: usize,
mtval: usize,
instruction_length: usize,
}

impl MmioAccessFault {
pub const ADDRESS_ALIGNMENT: usize = mem::size_of::<usize>();

pub fn new(cause: usize, mtval: usize, instruction_length: usize) -> Self {
Self { cause, mtval, instruction_length }
}

pub fn apply_to_confidential_hart(&self, confidential_hart: &mut ConfidentialHart) {
let mepc = confidential_hart.csrs().mepc.read_value() + self.instruction_length;
confidential_hart.csrs_mut().vsepc.set(mepc);
let trap_vector_address = confidential_hart.csrs().vstvec.read();
confidential_hart.csrs_mut().mepc.save_value(trap_vector_address);
confidential_hart.csrs_mut().vscause.set(self.cause);
confidential_hart.csrs_mut().vstval.set(self.mtval);
}

pub fn tried_to_access_valid_mmio_region(confidential_vm_id: ConfidentialVmId, fault_address: usize) -> bool {
ControlData::try_confidential_vm(confidential_vm_id, |confidential_vm| {
Ok(confidential_vm.is_mmio_region_defined(&ConfidentialVmMmioRegion::new(fault_address, Self::ADDRESS_ALIGNMENT)?))
})
.unwrap_or(false)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::mmio::MmioLoadPending;
use crate::confidential_flow::handlers::mmio::{MmioAccessFault, MmioLoadPending};
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::ConfidentialFlow;
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::is_bit_enabled;
use crate::core::architecture::specification::CAUSE_LOAD_ACCESS;
use crate::core::control_data::{ConfidentialHart, HypervisorHart, PendingRequest};
use crate::non_confidential_flow::DeclassifyToHypervisor;

Expand Down Expand Up @@ -32,6 +33,12 @@ impl MmioLoadRequest {
let instruction = self.mtinst | 0x3;
let instruction_length = if is_bit_enabled(self.mtinst, 1) { riscv_decode::instruction_length(instruction as u16) } else { 2 };

let fault_address = (self.mtval2 << 2) | (self.mtval & 0x3);
if !MmioAccessFault::tried_to_access_valid_mmio_region(confidential_flow.confidential_vm_id(), fault_address) {
let mmio_access_fault_handler = MmioAccessFault::new(CAUSE_LOAD_ACCESS.into(), self.mtval, instruction_length);
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::MmioAccessFault(mmio_access_fault_handler));
}

match crate::core::architecture::decode_result_register(instruction) {
Ok(gpr) => confidential_flow
.set_pending_request(PendingRequest::MmioLoad(MmioLoadPending::new(instruction_length, gpr)))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::mmio::MmioStorePending;
use crate::confidential_flow::handlers::mmio::{MmioAccessFault, MmioStorePending};
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::ConfidentialFlow;
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::specification::CAUSE_STORE_ACCESS;
use crate::core::architecture::{is_bit_enabled, GeneralPurposeRegister};
use crate::core::control_data::{ConfidentialHart, HypervisorHart, PendingRequest};
use crate::error::Error;
Expand Down Expand Up @@ -33,10 +34,17 @@ impl MmioStoreRequest {
let instruction_length = if is_bit_enabled(mtinst, 1) { riscv_decode::instruction_length(instruction as u16) } else { 2 };
let gpr = crate::core::architecture::decode_result_register(instruction);
let gpr_value = gpr.as_ref().and_then(|ref gpr| Ok(confidential_hart.gprs().read(**gpr))).unwrap_or(0);

Self { mcause, mtval, mtval2, mtinst, instruction_length, gpr, gpr_value }
}

pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
let fault_address = (self.mtval2 << 2) | (self.mtval & 0x3);
if !MmioAccessFault::tried_to_access_valid_mmio_region(confidential_flow.confidential_vm_id(), fault_address) {
let mmio_access_fault_handler = MmioAccessFault::new(CAUSE_STORE_ACCESS.into(), self.mtval, self.instruction_length);
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::MmioAccessFault(mmio_access_fault_handler));
}

match self.gpr {
Ok(_) => confidential_flow
.set_pending_request(PendingRequest::MmioStore(MmioStorePending::new(self.instruction_length)))
Expand Down
2 changes: 2 additions & 0 deletions security-monitor/src/confidential_flow/handlers/mmio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
pub use crate::confidential_flow::handlers::mmio::add_mmio_region::AddMmioRegion;
pub use crate::confidential_flow::handlers::mmio::mmio_access_fault::MmioAccessFault;
pub use crate::confidential_flow::handlers::mmio::mmio_load_pending::MmioLoadPending;
pub use crate::confidential_flow::handlers::mmio::mmio_load_request::MmioLoadRequest;
pub use crate::confidential_flow::handlers::mmio::mmio_load_response::MmioLoadResponse;
Expand All @@ -11,6 +12,7 @@ pub use crate::confidential_flow::handlers::mmio::mmio_store_response::MmioStore
pub use crate::confidential_flow::handlers::mmio::remove_mmio_region::RemoveMmioRegion;

mod add_mmio_region;
mod mmio_access_fault;
mod mmio_load_pending;
mod mmio_load_request;
mod mmio_load_response;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::handlers::sbi::{SbiRequest, SbiResponse};
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::sbi::CovgExtension;
use crate::core::architecture::GeneralPurposeRegister;
use crate::core::control_data::ConfidentialHart;
use crate::core::control_data::{ConfidentialHart, ConfidentialVmMmioRegion, ControlData, PendingRequest};
use crate::non_confidential_flow::DeclassifyToHypervisor;

pub struct RemoveMmioRegion {
region_start_address: usize,
Expand All @@ -20,7 +22,20 @@ impl RemoveMmioRegion {
}

pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
let transformation = ApplyToConfidentialHart::SbiResponse(SbiResponse::success());
confidential_flow.apply_and_exit_to_confidential_hart(transformation)
match ControlData::try_confidential_vm(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
Ok(confidential_vm.remove_mmio_region(&ConfidentialVmMmioRegion::new(self.region_start_address, self.region_length)?))
}) {
Ok(_) => confidential_flow
.set_pending_request(PendingRequest::SbiRequest())
.into_non_confidential_flow()
.declassify_and_exit_to_hypervisor(DeclassifyToHypervisor::SbiRequest(self.sbi_remove_mmio_region())),
Err(error) => {
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::SbiResponse(SbiResponse::error(error)))
}
}
}

fn sbi_remove_mmio_region(&self) -> SbiRequest {
SbiRequest::new(CovgExtension::EXTID, CovgExtension::SBI_EXT_COVG_REMOVE_MMIO_REGION, self.region_start_address, self.region_length)
}
}
73 changes: 59 additions & 14 deletions security-monitor/src/core/control_data/confidential_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::core::architecture::HartLifecycleState;
use crate::core::control_data::{
ConfidentialHart, ConfidentialHartRemoteCommand, ConfidentialVmId, ConfidentialVmMeasurement, HardwareHart,
ConfidentialHart, ConfidentialHartRemoteCommand, ConfidentialVmId, ConfidentialVmMeasurement, ConfidentialVmMmioRegion, HardwareHart,
};
use crate::core::interrupt_controller::InterruptController;
use crate::core::memory_protector::ConfidentialVmMemoryProtector;
Expand All @@ -19,15 +19,21 @@ pub struct ConfidentialVm {
confidential_hart_remote_commands: BTreeMap<usize, Mutex<Vec<ConfidentialHartRemoteCommand>>>,
memory_protector: ConfidentialVmMemoryProtector,
allowed_external_interrupts: usize,
mmio_regions: Vec<ConfidentialVmMmioRegion>,
}

impl ConfidentialVm {
pub const MAX_NUMBER_OF_HARTS_PER_VM: usize = 1024;

/// An average number of inter hart requests that can be buffered before being processed.
const AVG_NUMBER_OF_REMOTE_HART_REQUESTS: usize = 3;

/// A maximum number of inter hart requests that can be buffered.
const MAX_NUMBER_OF_REMOTE_HART_REQUESTS: usize = 64;

/// A maximum number of MMIO regions that a confidential VM can register
const MAX_NUMBER_OF_MMIO_REGIONS: usize = 1024;

/// Constructs a new confidential VM.
///
/// # Safety
Expand All @@ -45,9 +51,33 @@ impl ConfidentialVm {
(confidential_hart.confidential_hart_id(), Mutex::new(Vec::with_capacity(Self::AVG_NUMBER_OF_REMOTE_HART_REQUESTS)))
})
.collect();
Self { id, measurements, confidential_harts, memory_protector, confidential_hart_remote_commands, allowed_external_interrupts: 0 }
let mmio_regions = Vec::with_capacity(8);
Self {
id,
measurements,
confidential_harts,
memory_protector,
confidential_hart_remote_commands,
allowed_external_interrupts: 0,
mmio_regions,
}
}

pub fn confidential_vm_id(&self) -> ConfidentialVmId {
self.id
}

pub fn memory_protector_mut(&mut self) -> &mut ConfidentialVmMemoryProtector {
&mut self.memory_protector
}

pub(super) fn deallocate(self) {
self.memory_protector.into_root_page_table().deallocate();
}
}

/* Heavy context switches */
impl ConfidentialVm {
/// Assigns a confidential hart of the confidential VM to the hardware hart. The hardware memory isolation mechanism
/// is reconfigured to enforce memory access control for the confidential VM. Returns error if the confidential VM's
/// virtual hart has been already stolen or is in the `Stopped` state.
Expand Down Expand Up @@ -111,23 +141,39 @@ impl ConfidentialVm {
// finite state machine to the non-confidential part and the virtual hart is still assigned to the hardware hart.
unsafe { hardware_hart.hypervisor_hart().enable_hypervisor_memory_protector() };
}
}

pub fn confidential_vm_id(&self) -> ConfidentialVmId {
self.id
}

pub fn memory_protector_mut(&mut self) -> &mut ConfidentialVmMemoryProtector {
&mut self.memory_protector
}

/* Interrupt related */
impl ConfidentialVm {
pub fn allowed_external_interrupts(&self) -> usize {
self.allowed_external_interrupts
}

pub fn set_allowed_external_interrupts(&mut self, allowed_external_interrupts: usize) {
self.allowed_external_interrupts |= allowed_external_interrupts;
}
}

/* Management of untrusted MMIO regions */
impl ConfidentialVm {
pub fn add_mmio_region(&mut self, region: ConfidentialVmMmioRegion) -> Result<(), Error> {
ensure!(self.mmio_regions.len() < Self::MAX_NUMBER_OF_MMIO_REGIONS, Error::ReachedMaxNumberOfMmioRegions())?;
ensure!(!self.mmio_regions.iter().any(|x| x.overlaps(&region)), Error::OverlappingMmioRegion())?;
self.mmio_regions.push(region);
Ok(())
}

pub fn remove_mmio_region(&mut self, region: &ConfidentialVmMmioRegion) {
self.mmio_regions.retain(|x| !x.overlaps(region));
}

pub fn is_mmio_region_defined(&self, region: &ConfidentialVmMmioRegion) -> bool {
self.mmio_regions.iter().any(|x| x.contains(region))
}
}

/* Lifecycle related */
impl ConfidentialVm {
pub fn are_all_harts_shutdown(&self) -> bool {
self.confidential_harts.iter().filter(|hart| hart.lifecycle_state() != &HartLifecycleState::PoweredOff).count() == 0
}
Expand All @@ -144,7 +190,10 @@ impl ConfidentialVm {
let hart = self.confidential_harts.get_mut(confidential_hart_id).ok_or(Error::InvalidHartId())?;
hart.transition_from_stopped_to_started(start_address, opaque)
}
}

/* Remote commands */
impl ConfidentialVm {
/// Queues a request from one confidential hart to another and emits a hardware interrupt to the physical hart that
/// executes that confidential hart. If the confidential hart is not executing, then no hardware interrupt is
/// emmited.
Expand Down Expand Up @@ -184,10 +233,6 @@ impl ConfidentialVm {
})
}

pub(super) fn deallocate(self) {
self.memory_protector.into_root_page_table().deallocate();
}

pub fn try_confidential_hart_remote_commands<F, O>(&mut self, confidential_hart_id: usize, op: O) -> Result<F, Error>
where O: FnOnce(MutexGuard<'_, Vec<ConfidentialHartRemoteCommand>>) -> Result<F, Error> {
op(self.confidential_hart_remote_commands.get(&confidential_hart_id).ok_or(Error::InvalidHartId())?.lock())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;

#[derive(Debug)]
pub struct ConfidentialVmMmioRegion {
pub region_start_address: usize,
pub region_length: usize,
}

impl ConfidentialVmMmioRegion {
pub fn new(region_start_address: usize, region_length: usize) -> Result<Self, Error> {
// TODO: make sure region_start_address is aligned to 4KiB
// TODO: make sure the region_start_address is a valid guest address
Ok(Self { region_start_address, region_length })
}

pub fn overlaps(&self, other: &Self) -> bool {
self.region_start_address < other.region_start_address + other.region_length
&& other.region_start_address < self.region_start_address + self.region_length
}

pub fn contains(&self, other: &Self) -> bool {
self.region_start_address <= other.region_start_address
&& other.region_start_address + other.region_length < self.region_start_address + self.region_length
}
}
2 changes: 2 additions & 0 deletions security-monitor/src/core/control_data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use confidential_hart_remote_command::{ConfidentialHartRemoteCommand, Confid
pub use confidential_vm::ConfidentialVm;
pub use confidential_vm_id::ConfidentialVmId;
pub use confidential_vm_measurement::ConfidentialVmMeasurement;
pub use confidential_vm_mmio_region::ConfidentialVmMmioRegion;
pub use hardware_hart::{HardwareHart, HART_STACK_ADDRESS_OFFSET};
pub use hypervisor_hart::HypervisorHart;
pub use pending_request::PendingRequest;
Expand All @@ -16,6 +17,7 @@ mod confidential_hart_remote_command;
mod confidential_vm;
mod confidential_vm_id;
mod confidential_vm_measurement;
mod confidential_vm_mmio_region;
mod hardware_hart;
mod hypervisor_hart;
mod pending_request;
Expand Down
Loading
Loading