Skip to content

Commit

Permalink
Added stack monitoring and overflow detection to the SW emulator
Browse files Browse the repository at this point in the history
  • Loading branch information
clundin25 committed Dec 3, 2024
1 parent 3b50c6a commit a99ab23
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 8 deletions.
6 changes: 6 additions & 0 deletions hw-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mod rv32_builder;
pub use api::mailbox::mbox_write_fifo;
pub use api_types::{DeviceLifecycle, Fuses, SecurityState, U4};
pub use caliptra_emu_bus::BusMmio;
pub use caliptra_emu_cpu::{CodeRange, ImageInfo, StackInfo, StackRange};
use output::ExitStatus;
pub use output::Output;

Expand Down Expand Up @@ -176,6 +177,10 @@ pub struct InitParams<'a> {
// A trace path to use. If None, the CPTRA_TRACE_PATH environment variable
// will be used
pub trace_path: Option<PathBuf>,

// Information about the stack Caliptra is using. When set the emulator will check if the stack
// overflows.
pub stack_info: Option<StackInfo>,
}
impl<'a> Default for InitParams<'a> {
fn default() -> Self {
Expand Down Expand Up @@ -210,6 +215,7 @@ impl<'a> Default for InitParams<'a> {
}),
random_sram_puf: true,
trace_path: None,
stack_info: None,
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion hw-model/src/model_emulated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,13 @@ impl HwModel for ModelEmulated {
dccm_dest.copy_from_slice(params.dccm);
}
let soc_to_caliptra_bus = root_bus.soc_to_caliptra_bus();
let cpu = Cpu::new(BusLogger::new(root_bus), clock);
let cpu = {
let mut cpu = Cpu::new(BusLogger::new(root_bus), clock);
if let Some(stack_info) = params.stack_info {
cpu.with_stack_info(stack_info);
}
cpu
};

let mut hasher = DefaultHasher::new();
std::hash::Hash::hash_slice(params.rom, &mut hasher);
Expand Down
24 changes: 23 additions & 1 deletion rom/dev/tests/rom_integration_tests/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ use std::mem;

use caliptra_api::SocManager;
use caliptra_builder::{firmware, ImageOptions};
use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState};
use caliptra_common::{
memory_layout::{ROM_ORG, ROM_SIZE, ROM_STACK_ORG, ROM_STACK_SIZE, STACK_ORG, STACK_SIZE},
FMC_ORG, FMC_SIZE, RUNTIME_ORG, RUNTIME_SIZE,
};
use caliptra_hw_model::{
BootParams, CodeRange, Fuses, HwModel, ImageInfo, InitParams, SecurityState, StackInfo,
StackRange,
};
use caliptra_hw_model::{DefaultHwModel, ModelError};
use caliptra_image_types::ImageBundle;

Expand All @@ -18,10 +25,25 @@ pub fn build_hw_model_and_image_bundle(

pub fn build_hw_model(fuses: Fuses) -> DefaultHwModel {
let rom = caliptra_builder::build_firmware_rom(firmware::rom_from_env()).unwrap();
let image_info = vec![
ImageInfo::new(
StackRange::new(ROM_STACK_ORG + ROM_STACK_SIZE, ROM_STACK_ORG),
CodeRange::new(ROM_ORG, ROM_ORG + ROM_SIZE),
),
ImageInfo::new(
StackRange::new(STACK_ORG + STACK_SIZE, STACK_ORG),
CodeRange::new(FMC_ORG, FMC_ORG + FMC_SIZE),
),
ImageInfo::new(
StackRange::new(STACK_ORG + STACK_SIZE, STACK_ORG),
CodeRange::new(RUNTIME_ORG, RUNTIME_ORG + RUNTIME_SIZE),
),
];
caliptra_hw_model::new(
InitParams {
rom: &rom,
security_state: SecurityState::from(fuses.life_cycle as u32),
stack_info: Some(StackInfo::new(image_info)),
..Default::default()
},
BootParams {
Expand Down
30 changes: 26 additions & 4 deletions runtime/tests/runtime_integration_tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ use caliptra_builder::{
firmware::{APP_WITH_UART, APP_WITH_UART_FPGA, FMC_WITH_UART},
FwId, ImageOptions,
};
use caliptra_common::mailbox_api::{
CommandId, GetFmcAliasCertResp, GetRtAliasCertResp, InvokeDpeReq, InvokeDpeResp, MailboxReq,
MailboxReqHeader,
use caliptra_common::{
mailbox_api::{
CommandId, GetFmcAliasCertResp, GetRtAliasCertResp, InvokeDpeReq, InvokeDpeResp,
MailboxReq, MailboxReqHeader,
},
memory_layout::{ROM_ORG, ROM_SIZE, ROM_STACK_ORG, ROM_STACK_SIZE, STACK_ORG, STACK_SIZE},
FMC_ORG, FMC_SIZE, RUNTIME_ORG, RUNTIME_SIZE,
};
use caliptra_drivers::MfgFlags;
use caliptra_error::CaliptraError;
use caliptra_hw_model::{BootParams, DefaultHwModel, Fuses, HwModel, InitParams, ModelError};
use caliptra_hw_model::{
BootParams, CodeRange, DefaultHwModel, Fuses, HwModel, ImageInfo, InitParams, ModelError,
StackInfo, StackRange,
};
use dpe::{
commands::{Command, CommandHdr},
response::{
Expand Down Expand Up @@ -65,11 +72,26 @@ pub fn run_rt_test_lms(args: RuntimeTestArgs, lms_verify: bool) -> DefaultHwMode
opts
});

let image_info = vec![
ImageInfo::new(
StackRange::new(ROM_STACK_ORG + ROM_STACK_SIZE, ROM_STACK_ORG),
CodeRange::new(ROM_ORG, ROM_ORG + ROM_SIZE),
),
ImageInfo::new(
StackRange::new(STACK_ORG + STACK_SIZE, STACK_ORG),
CodeRange::new(FMC_ORG, FMC_ORG + FMC_SIZE),
),
ImageInfo::new(
StackRange::new(STACK_ORG + STACK_SIZE, STACK_ORG),
CodeRange::new(RUNTIME_ORG, RUNTIME_ORG + RUNTIME_SIZE),
),
];
let rom = caliptra_builder::rom_for_fw_integration_tests().unwrap();
let init_params = match args.init_params {
Some(init_params) => init_params,
None => InitParams {
rom: &rom,
stack_info: Some(StackInfo::new(image_info)),
..Default::default()
},
};
Expand Down
156 changes: 155 additions & 1 deletion sw-emulator/lib/cpu/src/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,124 @@ use caliptra_emu_types::{RvAddr, RvData, RvException, RvSize};

pub type InstrTracer<'a> = dyn FnMut(u32, RvInstr) + 'a;

/// Describes a Caliptra stack memory region
pub struct StackRange(u32, u32);
impl StackRange {
/// **Note:** `stack_start` MUST be greater than `stack_end`. Caliptra's stack grows
/// to a lower address.
pub fn new(stack_start: u32, stack_end: u32) -> Self {
if stack_start < stack_end {
panic!("Caliptra's stack grows to a lower address");
}
Self(stack_start, stack_end)
}
}

/// Describes a Caliptra code region
pub struct CodeRange(u32, u32);
impl CodeRange {
pub fn new(code_start: u32, code_end: u32) -> Self {
if code_start > code_end {
panic!("code grows to a higher address");
}
Self(code_start, code_end)
}
}

/// Contains metadata describing a Caliptra image
pub struct ImageInfo {
stack_range: StackRange,
code_range: CodeRange,
}

impl ImageInfo {
pub fn new(stack_range: StackRange, code_range: CodeRange) -> Self {
Self {
stack_range,
code_range,
}
}

/// Checks if the program counter is contained in `self`
///
/// returns `true` if the pc is in the image. `false` otherwise.
fn contains_pc(&self, pc: u32) -> bool {
self.code_range.0 <= pc && pc <= self.code_range.1
}

/// Checks if the stack pointer has overflowed.
///
/// Returns `Some(u32)` if an overflow has occurred. The `u32` is set
/// to the overflow amount.
///
/// Returns `None` if no overflow has occurred.
fn check_overflow(&self, sp: u32) -> Option<u32> {
let stack_end = self.stack_range.1;

// Stack grows to a lower address
if sp < stack_end {
let current_overflow = stack_end - sp;
Some(current_overflow)
} else {
None
}
}
}

/// Describes the shape of Caliptra's stack.
pub struct StackInfo {
images: Vec<ImageInfo>,
max_stack_overflow: u32,
has_overflowed: bool,
}

impl StackInfo {
/// Create a new `StackInfo` by describing the start and end of the stack and code segment for each
/// relevant Caliptra image.
pub fn new(images: Vec<ImageInfo>) -> Self {
Self {
images,
max_stack_overflow: 0,
has_overflowed: false,
}
}
}

impl StackInfo {
/// Fetch the largest stack overflow.
///
/// If the stack never overflowed, returns `None`.
fn max_stack_overflow(&self) -> Option<u32> {
if self.has_overflowed {
Some(self.max_stack_overflow)
} else {
None
}
}

/// Checks if the stack will overflow when pushed to `stack_address`.
///
/// Returns `true` if the stack will overflow, `false` if it will not overflow.
fn check_overflow(&mut self, pc: u32, stack_address: u32) -> Option<u32> {
if stack_address == 0 {
// sp is initialized to 0.
return None;
}

for image in self.images.iter() {
if image.contains_pc(pc) {
if let Some(overflow_amount) = image.check_overflow(stack_address) {
self.max_stack_overflow = self.max_stack_overflow.max(overflow_amount);
self.has_overflowed = true;
return Some(overflow_amount);
}
}
}

None
}
}

#[derive(Clone)]
pub struct CodeCoverage {
rom_bit_vec: BitVec,
Expand Down Expand Up @@ -159,6 +277,20 @@ pub struct Cpu<TBus: Bus> {
pub(crate) watch_ptr_cfg: WatchPtrCfg,

pub code_coverage: CodeCoverage,
stack_info: Option<StackInfo>,
}

impl<TBus: Bus> Drop for Cpu<TBus> {
fn drop(&mut self) {
if let Some(stack_info) = &self.stack_info {
if stack_info.has_overflowed {
panic!(
"[EMU] Fatal: Caliptra's stack overflowed by {} bytes!",
stack_info.max_stack_overflow().unwrap()
)
}
}
}
}

/// Cpu instruction step action
Expand Down Expand Up @@ -197,9 +329,14 @@ impl<TBus: Bus> Cpu<TBus> {
// TODO: Pass in code_coverage from the outside (as caliptra-emu-cpu
// isn't supposed to know anything about the caliptra memory map)
code_coverage: CodeCoverage::new(ROM_SIZE, ICCM_SIZE),
stack_info: None,
}
}

pub fn with_stack_info(&mut self, stack_info: StackInfo) {
self.stack_info = Some(stack_info);
}

/// Read the RISCV CPU Program counter
///
/// # Return
Expand Down Expand Up @@ -262,7 +399,24 @@ impl<TBus: Bus> Cpu<TBus> {
///
/// * `RvException` - Exception with cause `RvExceptionCause::IllegalRegister`
pub fn write_xreg(&mut self, reg: XReg, val: RvData) -> Result<(), RvException> {
self.xregs.write(reg, val)
// XReg::X2 is the sp register.
if reg == XReg::X2 {
self.check_stack(val);
}
self.xregs.write(reg, val)?;
Ok(())
}

// Check if the stack overflows at the requested address.
fn check_stack(&mut self, val: RvData) {
if let Some(stack_info) = &mut self.stack_info {
if let Some(overflow_amount) = stack_info.check_overflow(self.pc, val) {
eprintln!(
"[EMU] Caliptra's stack overflowed by {} bytes at pc 0x{:x}.",
overflow_amount, self.pc
);
}
}
}

/// Read the specified configuration status register
Expand Down
2 changes: 1 addition & 1 deletion sw-emulator/lib/cpu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub mod xreg_file;
pub use cpu::StepAction;
pub use cpu::WatchPtrHit;
pub use cpu::WatchPtrKind;
pub use cpu::{CoverageBitmaps, Cpu, InstrTracer};
pub use cpu::{CodeRange, CoverageBitmaps, Cpu, ImageInfo, InstrTracer, StackInfo, StackRange};
pub use csr_file::CsrFile;
pub use pic::{IntSource, Irq, Pic, PicMmioRegisters};
pub use types::RvInstr;

0 comments on commit a99ab23

Please sign in to comment.