Initial back-edge CFI implementation (#3606)

Give the user the option to sign and to authenticate function
return addresses with the operations introduced by the Pointer
Authentication extension to the Arm instruction set architecture.

Copyright (c) 2021, Arm Limited.
This commit is contained in:
Anton Kirilov
2022-08-03 19:08:29 +01:00
committed by GitHub
parent 709716bb8e
commit a897742593
17 changed files with 319 additions and 43 deletions

View File

@@ -10,7 +10,25 @@
//
// - AAPCS64 section 6.2.3 The Frame Pointer[0]
pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
*(fp as *mut usize).offset(1)
let mut pc = *(fp as *mut usize).offset(1);
// The return address might be signed, so we need to strip the highest bits
// (where the authentication code might be located) in order to obtain a
// valid address. We use the `XPACLRI` instruction, which is executed as a
// no-op by processors that do not support pointer authentication, so that
// the implementation is backward-compatible and there is no duplication.
// However, this instruction requires the LR register for both its input and
// output.
std::arch::asm!(
"mov lr, {pc}",
"xpaclri",
"mov {pc}, lr",
pc = inout(reg) pc,
out("lr") _,
options(nomem, nostack, preserves_flags, pure),
);
pc
}
pub unsafe fn get_next_older_fp_from_fp(fp: usize) -> usize {
*(fp as *mut usize)

View File

@@ -449,6 +449,18 @@ impl Engine {
{
enabled = match flag {
"has_lse" => Some(std::arch::is_aarch64_feature_detected!("lse")),
// No effect on its own, but in order to simplify the code on a
// platform without pointer authentication support we fail if
// "has_pauth" is enabled, but "sign_return_address" is not.
"has_pauth" => Some(std::arch::is_aarch64_feature_detected!("paca")),
// No effect on its own.
"sign_return_address_all" => Some(true),
// The pointer authentication instructions act as a `NOP` when
// unsupported (but keep in mind "has_pauth" as well), so it is
// safe to enable them.
"sign_return_address" => Some(true),
// No effect on its own.
"sign_return_address_with_bkey" => Some(true),
// fall through to the very bottom to indicate that support is
// not enabled to test whether this feature is enabled on the
// host.
@@ -582,18 +594,15 @@ mod tests {
assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
// FIXME(#1523) need debuginfo on aarch64 before we run this test there
if !cfg!(target_arch = "aarch64") {
let mut cfg = Config::new();
cfg.debug_info(true).cache_config_load(&config_path)?;
let engine = Engine::new(&cfg)?;
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
}
let mut cfg = Config::new();
cfg.debug_info(true).cache_config_load(&config_path)?;
let engine = Engine::new(&cfg)?;
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 0);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
Module::new(&engine, "(module (func))")?;
assert_eq!(engine.config().cache_config.cache_hits(), 1);
assert_eq!(engine.config().cache_config.cache_misses(), 1);
Ok(())
}