Skip to content

Commit

Permalink
Add physical memory protection in Keystone
Browse files Browse the repository at this point in the history
This commit adds memory protection to Keystone enclaves, effectively
making them confidential.

Making the enclave confidential requires more PMP, however OpenSBI gets
confused if it has less than 8 PMP registers but more than 0. So rather
than exposing 7 PMP registers we configure Miralis to expose 0.
  • Loading branch information
FredKhayat authored and CharlyCst committed Dec 16, 2024
1 parent f5b818f commit a99ae63
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 86 deletions.
2 changes: 1 addition & 1 deletion config/qemu-keystone.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ level = "info"
color = true

[vcpu]
max_pmp = 8
max_pmp = 0

[platform]
nb_harts = 1
Expand Down
2 changes: 1 addition & 1 deletion config/test/qemu-virt-keystone.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ color = true
max_firmware_exits = 2000

[vcpu]
max_pmp = 8
max_pmp = 0

[platform]
nb_harts = 1
Expand Down
155 changes: 89 additions & 66 deletions payload/test_keystone_payload/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,91 +3,114 @@
#![feature(start)]
// ———————————————————————————————— Guest OS ———————————————————————————————— //

use core::arch::global_asm;
use core::arch::{asm, global_asm};

use miralis_abi::{ecall3, log, setup_binary, success};
use miralis_abi::{ecall3, failure, log, setup_binary, success};

setup_binary!(main);

static mut EID: usize = 0; // Enclave ID

pub const ILLEGAL_ARGUMENT: usize = 100008;
pub const MIRALIS_KEYSTONE_EID: usize = 0x08424b45;
pub const CREATE_ENCLAVE_FID: usize = 2001;
pub const DESTROY_ENCLAVE_FID: usize = 2002;
pub const RUN_ENCLAVE_FID: usize = 2003;
pub const RESUME_ENCLAVE_FID: usize = 2005;
pub const ERR_ENCLAVE_INTERRUPTED: usize = 100002;
pub const ERR_ENCLAVE_EDGE_CALL_HOST: usize = 100011;

#[repr(C)]
struct CreateArgs {
epm_paddr: usize,
epm_size: usize,
utm_paddr: usize,
utm_size: usize,
runtime_paddr: usize,
user_paddr: usize,
free_paddr: usize,
free_requested: usize,
}

fn main() -> ! {
log::info!("Hello from test keystone payload");

pub const ILLEGAL_ARGUMENT: usize = 100008;
pub const MIRALIS_KEYSTONE_EID: usize = 0x08424b45;
pub const CREATE_ENCLAVE_FID: usize = 2001;
pub const RUN_ENCLAVE_FID: usize = 2003;
pub const RESUME_ENCLAVE_FID: usize = 2005;
pub const ERR_ENCLAVE_INTERRUPTED: usize = 100002;
pub const ERR_ENCLAVE_EDGE_CALL_HOST: usize = 100011;

// Test create enclave
#[repr(C)]
struct CreateArgs {
epm_paddr: usize,
epm_size: usize,
utm_paddr: usize,
utm_size: usize,
runtime_paddr: usize,
user_paddr: usize,
free_paddr: usize,
free_requested: usize,
}

let valid_args = CreateArgs {
// Values copied from keystone
epm_paddr: _enclave as usize,
epm_size: 0x200000,
utm_paddr: 0x83240000,
utm_size: 0x40000,
runtime_paddr: 0x8380C000,
user_paddr: 0x83834000,
free_paddr: 0x838D6000,
free_requested: 0x40000,
};

// Keystone should return SUCCESS if given valid arguments
let eid = unsafe {
ecall3(
unsafe {
// Miralis should not crash if given an invalid argument
let err = ecall3(
MIRALIS_KEYSTONE_EID,
CREATE_ENCLAVE_FID,
&valid_args as *const CreateArgs as usize,
0xDEADBEEFDEADBEEF,
0,
0,
)
}
.expect("Failed to create enclave");
log::info!("Enclave created successfully");

// Miralis should not crash if given an invalid argument
let err = unsafe {
ecall3(
.unwrap_err();
assert_eq!(err, ILLEGAL_ARGUMENT);
log::info!("Illegal argument test passed");

let shared_memory: [usize; 64] = [0; 64];
let valid_args = CreateArgs {
epm_paddr: _enclave as usize,
epm_size: 0x128,
utm_paddr: shared_memory.as_ptr() as usize,
utm_size: shared_memory.len(),
runtime_paddr: 0x8380C000,
user_paddr: 0x83834000,
free_paddr: 0x838D6000,
free_requested: 0x40000,
};

// Keystone should return SUCCESS if given valid arguments
EID = ecall3(
MIRALIS_KEYSTONE_EID,
CREATE_ENCLAVE_FID,
0xDEADBEEFDEADBEEF,
&valid_args as *const CreateArgs as usize,
0,
0,
)
.expect("Failed to create enclave");
log::info!("Enclave created successfully");

// Run the enclave
let mut result = ecall3(MIRALIS_KEYSTONE_EID, RUN_ENCLAVE_FID, EID, 0, 0);
let mut max_exits = 100;
log::info!("Enclave ran successfully");
while result.is_err() {
max_exits -= 1;
assert!(
result.unwrap_err() == ERR_ENCLAVE_INTERRUPTED
|| result.unwrap_err() == ERR_ENCLAVE_EDGE_CALL_HOST
);
assert!(max_exits > 0, "Enclave exited too many times");
result = ecall3(MIRALIS_KEYSTONE_EID, RESUME_ENCLAVE_FID, EID, 0, 0);
}

assert_eq!(result.unwrap(), 0xBEEF);
log::info!("Enclave exited successfully");

// Set up a trap handler to catch load access faults
asm!(
"csrw stvec, {trap_handler}",
trap_handler = in(reg) trap_handler as usize & !0b11);

// Try to access the enclave memory. This should trigger a trap.
let y = *(_enclave as *const usize);

log::info!("The enclave memory is not protected: {:x}.", y);
failure();
}
.unwrap_err();
assert_eq!(err, ILLEGAL_ARGUMENT);
log::info!("Illegal argument test passed");

// Run the enclave
let mut result = unsafe { ecall3(MIRALIS_KEYSTONE_EID, RUN_ENCLAVE_FID, eid, 0, 0) };
let mut max_exits = 100;
log::info!("Enclave ran successfully");
while result.is_err() {
max_exits -= 1;
assert!(
result.unwrap_err() == ERR_ENCLAVE_INTERRUPTED
|| result.unwrap_err() == ERR_ENCLAVE_EDGE_CALL_HOST
);
assert!(max_exits > 0, "Enclave exited too many times");
result = unsafe { ecall3(MIRALIS_KEYSTONE_EID, RESUME_ENCLAVE_FID, eid, 0, 0) };
}
}

unsafe fn trap_handler() {
log::info!("The enclave memory is protected.");

// Destroy the enclave
ecall3(MIRALIS_KEYSTONE_EID, DESTROY_ENCLAVE_FID, EID, 0, 0)
.expect("Failed to destroy enclave");
log::info!("Enclave destroyed successfully");

assert_eq!(result.unwrap(), 0xBEEF);
log::info!("Enclave exited successfully");
let y = *(_enclave as *const usize);
log::info!("The enclave is no longer protected: {:x}.", y);
success()
}

Expand Down
2 changes: 1 addition & 1 deletion src/arch/pmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ impl PmpGroup {
// ————————————————————————————— Memory Segment ————————————————————————————— //

/// A segment of memory.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Segment {
start: usize,
size: usize,
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub const DELEGATE_PERF_COUNTER: bool = is_enabled_default_false!("MIRALIS_DELEG
pub const PLATFORM_BOOT_HART_ID: usize =
parse_usize_or(option_env!("MIRALIS_PLATFORM_BOOT_HART_ID"), 0);

/// Whether any benchmark is enable
/// Whether any benchmark is enabled
pub const BENCHMARK: bool = is_enabled!("MIRALIS_BENCHMARK");

/// Whether print in csv format or not
Expand Down Expand Up @@ -97,7 +97,7 @@ pub const TARGET_FIRMWARE_ADDRESS: usize =

/// Start address of the payload
pub const TARGET_PAYLOAD_ADDRESS: usize =
parse_usize_or(option_env!("MIRALIS_TARGET_PAYLAOD_ADDRESS"), 0x80400000);
parse_usize_or(option_env!("MIRALIS_TARGET_PAYLOAD_ADDRESS"), 0x80400000);

/// The stack size for each Miralis thread (one per hart)
pub const TARGET_STACK_SIZE: usize =
Expand Down
Loading

0 comments on commit a99ae63

Please sign in to comment.