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 2, 2024
1 parent 3b50c6a commit d6f8397
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 18 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::StackInfo;
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
27 changes: 24 additions & 3 deletions rom/dev/tests/rom_integration_tests/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,41 @@ use std::mem;

use caliptra_api::SocManager;
use caliptra_builder::{firmware, ImageOptions};
use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState};
use caliptra_drivers::memory_layout::{ROM_STACK_ORG, ROM_STACK_SIZE};
use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState, StackInfo};
use caliptra_hw_model::{DefaultHwModel, ModelError};
use caliptra_image_types::ImageBundle;

pub fn build_hw_model_and_image_bundle(
fuses: Fuses,
image_options: ImageOptions,
) -> (DefaultHwModel, ImageBundle) {
build_hw_model_and_image_bundle_with_stack_info(fuses, image_options, None)
}

pub fn build_hw_model_and_image_bundle_with_stack_info(
fuses: Fuses,
image_options: ImageOptions,
stack_info: Option<StackInfo>,
) -> (DefaultHwModel, ImageBundle) {
let image = build_image_bundle(image_options);
(build_hw_model(fuses), image)
(build_hw_model_with_stack_info(fuses, stack_info), image)
}

pub fn build_hw_model(fuses: Fuses) -> DefaultHwModel {
pub fn build_hw_model_with_stack_info(
fuses: Fuses,
stack_info: Option<StackInfo>,
) -> DefaultHwModel {
let rom = caliptra_builder::build_firmware_rom(firmware::rom_from_env()).unwrap();
let stack_info = stack_info.unwrap_or(StackInfo::new(
ROM_STACK_ORG + ROM_STACK_SIZE,
ROM_STACK_ORG,
));
caliptra_hw_model::new(
InitParams {
rom: &rom,
security_state: SecurityState::from(fuses.life_cycle as u32),
stack_info: Some(stack_info),
..Default::default()
},
BootParams {
Expand All @@ -32,6 +49,10 @@ pub fn build_hw_model(fuses: Fuses) -> DefaultHwModel {
.unwrap()
}

pub fn build_hw_model(fuses: Fuses) -> DefaultHwModel {
build_hw_model_with_stack_info(fuses, None)
}

pub fn build_image_bundle(image_options: ImageOptions) -> ImageBundle {
caliptra_builder::build_and_sign_image(
&firmware::FMC_WITH_UART,
Expand Down
24 changes: 17 additions & 7 deletions rom/dev/tests/rom_integration_tests/test_idevid_derivation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
use caliptra_api::SocManager;
use caliptra_builder::{firmware, ImageOptions};
use caliptra_common::mailbox_api::{CommandId, GetLdevCertResp, MailboxReqHeader};
use caliptra_drivers::memory_layout::{STACK_ORG, STACK_SIZE};
use caliptra_drivers::{IdevidCertAttr, MfgFlags, X509KeyIdAlgo};
use caliptra_hw_model::{DefaultHwModel, Fuses, HwModel};
use caliptra_hw_model::{DefaultHwModel, Fuses, HwModel, StackInfo};
use caliptra_image_types::ImageBundle;
use openssl::pkey::{PKey, Public};
use openssl::x509::X509;
Expand Down Expand Up @@ -43,8 +44,11 @@ fn generate_csr(hw: &mut DefaultHwModel, image_bundle: &ImageBundle) -> Vec<u8>

#[test]
fn test_generate_csr() {
let (mut hw, image_bundle) =
helpers::build_hw_model_and_image_bundle(Fuses::default(), ImageOptions::default());
let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info(
Fuses::default(),
ImageOptions::default(),
Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)),
);
generate_csr(&mut hw, &image_bundle);
}

Expand All @@ -54,8 +58,11 @@ fn test_idev_subj_key_id_algo() {
let mut fuses = Fuses::default();
fuses.idevid_cert_attr[IdevidCertAttr::Flags as usize] = algo;

let (mut hw, image_bundle) =
helpers::build_hw_model_and_image_bundle(fuses, ImageOptions::default());
let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info(
Fuses::default(),
ImageOptions::default(),
Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)),
);
hw.upload_firmware(&image_bundle.to_bytes().unwrap())
.unwrap();

Expand Down Expand Up @@ -88,8 +95,11 @@ fn test_generate_csr_stress() {

for _ in 0..num_tests {
let fuses = fuses_with_random_uds();
let (mut hw, image_bundle) =
helpers::build_hw_model_and_image_bundle(fuses.clone(), ImageOptions::default());
let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info(
Fuses::default(),
ImageOptions::default(),
Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)),
);

let csr_bytes = generate_csr(&mut hw, &image_bundle);

Expand Down
10 changes: 7 additions & 3 deletions rom/dev/tests/rom_integration_tests/test_warm_reset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use caliptra_builder::firmware::{APP_WITH_UART, ROM_WITH_UART};
use caliptra_builder::ImageOptions;
use caliptra_common::mailbox_api::CommandId;
use caliptra_common::RomBootStatus::*;
use caliptra_drivers::memory_layout::{STACK_ORG, STACK_SIZE};
use caliptra_drivers::CaliptraError;
use caliptra_hw_model::DeviceLifecycle;
use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState};
use caliptra_hw_model::{BootParams, Fuses, HwModel, InitParams, SecurityState, StackInfo};
use caliptra_test::swap_word_bytes_inplace;
use openssl::sha::sha384;
use zerocopy::AsBytes;
Expand Down Expand Up @@ -178,8 +179,11 @@ fn test_warm_reset_during_update_reset() {
..Default::default()
};

let (mut hw, image_bundle) =
helpers::build_hw_model_and_image_bundle(fuses, ImageOptions::default());
let (mut hw, image_bundle) = helpers::build_hw_model_and_image_bundle_with_stack_info(
fuses,
ImageOptions::default(),
Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)),
);

hw.upload_firmware(&image_bundle.to_bytes().unwrap())
.unwrap();
Expand Down
10 changes: 8 additions & 2 deletions runtime/tests/runtime_integration_tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ use caliptra_common::mailbox_api::{
CommandId, GetFmcAliasCertResp, GetRtAliasCertResp, InvokeDpeReq, InvokeDpeResp, MailboxReq,
MailboxReqHeader,
};
use caliptra_drivers::MfgFlags;
use caliptra_drivers::{
memory_layout::{STACK_ORG, STACK_SIZE},
MfgFlags,
};
use caliptra_error::CaliptraError;
use caliptra_hw_model::{BootParams, DefaultHwModel, Fuses, HwModel, InitParams, ModelError};
use caliptra_hw_model::{
BootParams, DefaultHwModel, Fuses, HwModel, InitParams, ModelError, StackInfo,
};
use dpe::{
commands::{Command, CommandHdr},
response::{
Expand Down Expand Up @@ -70,6 +75,7 @@ pub fn run_rt_test_lms(args: RuntimeTestArgs, lms_verify: bool) -> DefaultHwMode
Some(init_params) => init_params,
None => InitParams {
rom: &rom,
stack_info: Some(StackInfo::new(STACK_ORG + STACK_SIZE, STACK_ORG)),
..Default::default()
},
};
Expand Down
110 changes: 109 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,77 @@ use caliptra_emu_types::{RvAddr, RvData, RvException, RvSize};

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

/// Describes the shape of Caliptra's stack.
///
/// Currently only supports one stack region at a time.
pub struct StackInfo {
lowest_stack_address: u32,
current_stack_address: u32,
has_overflowed: bool,
stack_start: u32,
stack_end: u32,
}

impl StackInfo {
/// Create a new `StackInfo` by describing the start and end of the stack
///
/// **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 {
lowest_stack_address: stack_start,
current_stack_address: stack_start,
has_overflowed: false,
stack_start,
stack_end,
}
}
}

impl StackInfo {
/// Fetch current stack usage.
pub fn current_stack_usage(&self) -> u32 {
self.stack_start - self.current_stack_address
}

/// Fetch the largest stack overflow.
///
/// If the stack never overflowed, returns `None`.
pub fn max_stack_overflow(&self) -> Option<u32> {
if self.has_overflowed {
Some(self.stack_end - self.lowest_stack_address)
} 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.
pub fn check_overflow(&mut self, stack_address: u32) -> bool {
if stack_address == 0 {
// sp is initialized to 0.
return false;
}

self.current_stack_address = stack_address;
if self.current_stack_address < self.lowest_stack_address {
self.lowest_stack_address = self.current_stack_address;
}

if self.current_stack_address < self.stack_end {
self.has_overflowed = true;
return true;
}

false
}
}

#[derive(Clone)]
pub struct CodeCoverage {
rom_bit_vec: BitVec,
Expand Down Expand Up @@ -159,6 +230,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 +282,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 +352,25 @@ 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 stack_info.check_overflow(val) {
eprintln!(
"[EMU] Caliptra's stack overflowed by {} bytes at pc 0x{:x}.",
stack_info.max_stack_overflow().unwrap(),
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::{CoverageBitmaps, Cpu, InstrTracer, StackInfo};
pub use csr_file::CsrFile;
pub use pic::{IntSource, Irq, Pic, PicMmioRegisters};
pub use types::RvInstr;

0 comments on commit d6f8397

Please sign in to comment.