Skip to content

Commit

Permalink
[feat] Control Flow Integrity (CFI) Features (#603)
Browse files Browse the repository at this point in the history
 This PR contains the following changes:

 1. CFI library with following features:
	a. CFI counter to detect out-of-flow code execution.
	b. XOR based Random number generator.
	c. Procedural macro to add random delays before and after decorated functions
	   and to detect out-of-flow code execution.
	d. Variable launder function for assisting with duplicate checks.

 2. Enablement of ROM code with CFI features.
  • Loading branch information
mhatrevi authored Aug 14, 2023
1 parent 1b40bea commit b2dc647
Showing 30 changed files with 942 additions and 31 deletions.
23 changes: 23 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,6 +19,8 @@ exclude = [

members = [
"builder",
"cfi/lib",
"cfi/derive",
"ci-tools/file-header-fix",
"ci-tools/size-history",
"common",
@@ -74,6 +76,8 @@ arrayref = "0.3.6"
asn1 = "0.13.0"
bitfield = "0.14.0"
bitflags = "2.0.1"
caliptra-cfi-lib = { path = "cfi/lib", default-features = false, features = ["cfi", "cfi-counter" ] }
caliptra-cfi-derive = { path = "cfi/derive" }
caliptra_common = { path = "common", default-features = false }
caliptra-builder = { path = "builder" }
caliptra-cpu = { path = "cpu" }
16 changes: 16 additions & 0 deletions cfi/derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Licensed under the Apache-2.0 license

[package]
name = "caliptra-cfi-derive"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true
doctest = false

[dependencies]
syn = { version = "1.0.107", features = ["extra-traits", "full"] }
quote = "1.0.23"
proc-macro2 = "1.0.51"
paste = "1.0.11"
79 changes: 79 additions & 0 deletions cfi/derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*++
Licensed under the Apache-2.0 license.
File Name:
lib.rs
Abstract:
File contains CFI procedural macro.
References:
https://tf-m-user-guide.trustedfirmware.org/design_docs/tfm_physical_attack_mitigation.html
https://github.com/rust-embedded/riscv/blob/master/src/asm.rs
--*/

use proc_macro::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::__private::TokenStream2;
use syn::parse_macro_input;
use syn::parse_quote;
use syn::FnArg;
use syn::ItemFn;

#[proc_macro_attribute]
pub fn cfi_mod_fn(_args: TokenStream, input: TokenStream) -> TokenStream {
cfi_fn(true, input)
}

#[proc_macro_attribute]
pub fn cfi_impl_fn(_args: TokenStream, input: TokenStream) -> TokenStream {
cfi_fn(false, input)
}

fn cfi_fn(mod_fn: bool, input: TokenStream) -> TokenStream {
let mut wrapper_fn: ItemFn = parse_macro_input!(input as ItemFn);
let mut orig_fn = wrapper_fn.clone();
orig_fn.sig.ident = format_ident!("__cfi_{}", wrapper_fn.sig.ident);
orig_fn.attrs.clear();
orig_fn.vis = syn::Visibility::Inherited;

let fn_name = format_ident!("{}", orig_fn.sig.ident);

let param_names: Vec<TokenStream2> = orig_fn
.sig
.inputs
.iter()
.map(|input| match input {
FnArg::Receiver(r) => r.self_token.to_token_stream(),
FnArg::Typed(p) => p.pat.to_token_stream(),
})
.collect();

let fn_call = if mod_fn {
quote!(#fn_name( #(#param_names,)* ))
} else {
quote!(Self::#fn_name( #(#param_names,)* ))
};

wrapper_fn.block.stmts.clear();
wrapper_fn.block.stmts = parse_quote!(
let saved_ctr = caliptra_cfi_lib::CfiCounter::increment();
caliptra_cfi_lib::CfiCounter::delay();
let ret = #fn_call;
caliptra_cfi_lib::CfiCounter::delay();
let new_ctr = caliptra_cfi_lib::CfiCounter::decrement();
caliptra_cfi_lib::CfiCounter::assert_eq(saved_ctr, new_ctr);
ret
);

let code = quote! {
#wrapper_fn
#orig_fn
};

code.into()
}
25 changes: 25 additions & 0 deletions cfi/lib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Licensed under the Apache-2.0 license

[package]
name = "caliptra-cfi-lib"
version = "0.1.0"
edition = "2021"

[lib]
doctest = false

[dependencies]
caliptra_common = { workspace = true, default-features = false }
caliptra-drivers.workspace = true
caliptra-registers.workspace = true
ufmt.workspace = true

[dev-dependencies]
caliptra-cfi-derive.workspace = true

[features]
default = ["cfi", "cfi-counter", "cfi-test"]
cfi = []
cfi-counter = []
cfi-test = []

179 changes: 179 additions & 0 deletions cfi/lib/src/cfi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*++
Licensed under the Apache-2.0 license.
File Name:
cfi.rs
Abstract:
File contains CFI launder implementation.
References:
https://github.com/lowRISC/opentitan/blob/7a61300cf7c409fa68fd892942c1d7b58a7cd4c0/sw/device/lib/base/hardened.h#L260
--*/

use caliptra_drivers::CaliptraError;
use core::cfg;
use core::cmp::{Eq, Ord, PartialEq, PartialOrd};
use core::marker::Copy;

/// CFI Panic Information
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum CfiPanicInfo {
/// CFI Counter decode error
CounterCorrupt,

/// CFI Counter overflow
CounterOverflow,

/// CFI Counter underflow
CounterUnderflow,

/// CFI Counter mismatch
CounterMismatch,

/// CFI Assert Equal failed
AssertEqFail,

/// CFI Assert Not Equal failed
AssertNeFail,

/// CFI Greater Than failed
AssertGtFail,

/// CFI Less Than failed
AssertLtFail,

/// CFI Greater Than Equal failed
AssertGeFail,

/// CFI Less Than Equal failed
AssertLeFail,

/// Random number generator error
TrngError,

/// Unknown error
UnknownError,
}

impl From<CfiPanicInfo> for CaliptraError {
/// Converts to this type from the input type.
fn from(info: CfiPanicInfo) -> CaliptraError {
match info {
CfiPanicInfo::CounterCorrupt => CaliptraError::ROM_CFI_PANIC_COUNTER_CORRUPT,
CfiPanicInfo::CounterOverflow => CaliptraError::ROM_CFI_PANIC_COUNTER_OVERFLOW,
CfiPanicInfo::CounterUnderflow => CaliptraError::ROM_CFI_PANIC_COUNTER_UNDERFLOW,
CfiPanicInfo::CounterMismatch => CaliptraError::ROM_CFI_PANIC_COUNTER_MISMATCH,
CfiPanicInfo::AssertEqFail => CaliptraError::ROM_CFI_PANIC_ASSERT_EQ_FAILURE,
CfiPanicInfo::AssertNeFail => CaliptraError::ROM_CFI_PANIC_ASSERT_NE_FAILURE,
CfiPanicInfo::AssertGtFail => CaliptraError::ROM_CFI_PANIC_ASSERT_GT_FAILURE,
CfiPanicInfo::AssertLtFail => CaliptraError::ROM_CFI_PANIC_ASSERT_LT_FAILURE,
CfiPanicInfo::AssertGeFail => CaliptraError::ROM_CFI_PANIC_ASSERT_GE_FAILURE,
CfiPanicInfo::AssertLeFail => CaliptraError::ROM_CFI_PANIC_ASSERT_LE_FAILURE,
CfiPanicInfo::TrngError => CaliptraError::ROM_CFI_PANIC_TRNG_FAILURE,
_ => CaliptraError::ROM_CFI_PANIC_UNKNOWN,
}
}
}

/// Launder the value to prevent compiler optimization
///
/// # Arguments
///
/// * `val` - Value to launder
///
/// # Returns
///
/// `T` - Same value
pub fn cfi_launder<T>(val: T) -> T {
if cfg!(feature = "cfi") {
// Note: The black box seems to be disabling more optimization
// than necessary and results in larger binary size
core::hint::black_box(val)
} else {
val
}
}

/// Control flow integrity panic
///
/// This panic is raised when the control flow integrity error is detected
///
/// # Arguments
///
/// * `info` - Panic information
///
/// # Returns
///
/// `!` - Never returns
#[inline(never)]
pub fn cfi_panic(info: CfiPanicInfo) -> ! {
// Prevent the compiler from optimizing the reason
let _ = cfi_launder(info);

#[cfg(feature = "cfi")]
{
#[cfg(feature = "cfi-test")]
{
panic!("CFI Panic = {:04x?}", info);
}

#[cfg(not(feature = "cfi-test"))]
{
extern "C" {
fn cfi_panic_handler(code: u32) -> !;
}
unsafe {
cfi_panic_handler(CaliptraError::from(info).into());
}
}
}

#[cfg(not(feature = "cfi"))]
{
unimplemented!()
}
}

macro_rules! cfi_assert_macro {
($name: ident, $op: tt, $trait1: path, $trait2: path, $panic_info: ident) => {
/// CFI Binary Condition Assertion
///
/// # Arguments
///
/// `a` - Left hand side
/// `b` - Right hand side
#[inline(never)]
#[allow(unused)]
pub fn $name<T>(lhs: T, rhs: T)
where
T: $trait1 + $trait2,
{
if cfg!(feature = "cfi") {
if !(lhs $op rhs) {
cfi_panic(CfiPanicInfo::$panic_info);
}
} else {
lhs $op rhs;
}
}
};
}

cfi_assert_macro!(cfi_assert_eq, ==, Eq, PartialEq, AssertEqFail);
cfi_assert_macro!(cfi_assert_ne, !=, Eq, PartialEq, AssertNeFail);
cfi_assert_macro!(cfi_assert_gt, >, Ord, PartialOrd, AssertGtFail);
cfi_assert_macro!(cfi_assert_lt, <, Ord, PartialOrd, AssertLtFail);
cfi_assert_macro!(cfi_assert_ge, >=, Ord, PartialOrd, AssertGeFail);
cfi_assert_macro!(cfi_assert_le, <=, Ord, PartialOrd, AssertLeFail);

#[macro_export]
macro_rules! cfi_assert {
($cond: expr) => {
cfi_assert_eq($cond, true)
};
}
Loading

0 comments on commit b2dc647

Please sign in to comment.