-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
cfi: Simpler launder implementation for common types (#1714)
* `cfi_launder` is meant to prevent the Rust compiler from optimizing a value away. Our current implementation uses `core::hint::black_box()`, which is the recommended way in Rust. The problem is, this appears to often force the argument to spill into memory and to be reloaded, which can be a lot of extra instructions. The original inspiration for this function is from, I believe, [OpenTitan's launder* functions](https://github.com/lowRISC/opentitan/blob/master/sw/device/lib/base/hardened.h#L193). There, they use an LLVM-specific trick of a blank inline assembly block to force the compiler to keep the argument in a register. After reviewing our code and speaking with @vsonims, it sounds like the intention of the launder in our code is to prevent the compiler from optimizing the value away (as the comments suggest), so the simpler inline assembly trick may be sufficient (since we use the official Rust compiler, which uses LLVM). The biggest problem is that we launder many types of values in our code and not all of them fit into a register. So, this PR represents an incremental change: for `u32`s and similar small types, we implement `cfi_launder` using the inline assembly trick from OpenTitan. For any other types, we have a trait that can be derived that will call `core::hint::black_box` in the same way as today. We can do future follow-up PRs to try to try to clean up some of those other uses of `cfi_launder` to hopefully shrink the code more. I also slipped in avoid a few extra copies in the verifier by using references instead of copies (this saves ~80 bytes of instruction space). This PR appears to shrink the ROM code size by 1232 bytes and the runtime firmware by 700 bytes. (cherry picked from commit 571d253)
- Loading branch information
Showing
16 changed files
with
272 additions
and
42 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# WARNING: Do not update this file without the approval of the Caliptra TAC | ||
9537318fd30c3e3d341cffab5721ba3242810be85e79ed9dc644c47c062555ef7519fb48857745e7ccb9917ac1fe120a caliptra-rom-no-log.bin | ||
e4e74d2d1c4794b950a548072fc8dc4c9ab64aba7a01ae400e9fe66c64b43f715e72dc430e7318496009ebedd0412bc6 caliptra-rom-with-log.bin | ||
ad1064cba5b190e4f5258c175b7ec7c697ccb188ba0ed18302c6e924f0ea0b10457dc753d6d2963f415fbaf761eace96 caliptra-rom-no-log.bin | ||
4d93a6856a0cba636a5d2a9c116393f56dc3b34113f7149ae62325772c1eacb08b1d78424771cc71197252abe603eb92 caliptra-rom-with-log.bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Licensed under the Apache-2.0 license | ||
|
||
// These tests are here so that they are excluded in FPGA tests. | ||
|
||
// These tests don't directly import the CFI code. If they fail, | ||
// this likely indicates that the CFI laundering code may not | ||
// be doing what we want, and we need to investigate. | ||
|
||
#[cfg(test)] | ||
mod test { | ||
|
||
const START: &str = " | ||
#![no_std] | ||
pub fn add(mut a: u32, mut b: u32) -> u32 { | ||
launder(a) + launder(a) + launder(b) + launder(b) | ||
} | ||
"; | ||
|
||
const LAUNDER: &str = " | ||
#[inline(always)] | ||
fn launder(mut val: u32) -> u32 { | ||
// Safety: this is a no-op, since we don't modify the input. | ||
unsafe { | ||
core::arch::asm!( | ||
\"/* {t} */\", | ||
t = inout(reg) val, | ||
); | ||
} | ||
val | ||
}"; | ||
|
||
const NO_LAUNDER: &str = " | ||
#[inline(always)] | ||
fn launder(mut val: u32) -> u32 { | ||
val | ||
} | ||
"; | ||
|
||
fn compile_to_riscv32_asm(src: String) -> String { | ||
let dir = std::env::temp_dir(); | ||
let src_path = dir.join("asm.rs"); | ||
let dst_path = dir.join("asm.s"); | ||
|
||
std::fs::write(src_path.clone(), src).expect("could not write asm file"); | ||
|
||
let p = std::process::Command::new("rustc") | ||
.args([ | ||
"--crate-type=lib", | ||
"--target", | ||
"riscv32imc-unknown-none-elf", | ||
"-C", | ||
"opt-level=s", | ||
"--emit", | ||
"asm", | ||
src_path.to_str().expect("could not convert path"), | ||
"-o", | ||
dst_path.to_str().expect("could not convert path"), | ||
]) | ||
.output() | ||
.expect("failed to compile"); | ||
assert!(p.status.success()); | ||
std::fs::read_to_string(dst_path).expect("could not read asm file") | ||
} | ||
|
||
#[test] | ||
fn test_launder() { | ||
// With no laundering, LLVM can simplify the double add to a shift left. | ||
let src = format!("{}{}", START, NO_LAUNDER); | ||
let asm = compile_to_riscv32_asm(src); | ||
assert!(asm.contains("sll")); | ||
|
||
// With laundering, LLVM cannot simplify the double add and has to use the register twice. | ||
let src = format!("{}{}", START, LAUNDER); | ||
let asm = compile_to_riscv32_asm(src); | ||
assert!(!asm.contains("sll")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.