Skip to content

Add codegen option for branch protection and pointer authentication on AArch64 #88354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use rustc_middle::ty::layout::{
};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::config::{CFGuard, CrateType, DebugInfo};
use rustc_session::config::{BranchProtection, CFGuard, CrateType, DebugInfo, PAuthKey, PacRet};
use rustc_session::Session;
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
Expand Down Expand Up @@ -242,6 +242,34 @@ pub unsafe fn create_module(
}
}

if sess.target.arch == "aarch64" {
let BranchProtection { bti, pac_ret: pac } = sess.opts.debugging_opts.branch_protection;

llvm::LLVMRustAddModuleFlag(
llmod,
"branch-target-enforcement\0".as_ptr().cast(),
bti.into(),
);

llvm::LLVMRustAddModuleFlag(
llmod,
"sign-return-address\0".as_ptr().cast(),
pac.is_some().into(),
);
let pac_opts = pac.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
llvm::LLVMRustAddModuleFlag(
llmod,
"sign-return-address-all\0".as_ptr().cast(),
pac_opts.leaf.into(),
);
let is_bkey = if pac_opts.key == PAuthKey::A { false } else { true };
llvm::LLVMRustAddModuleFlag(
llmod,
"sign-return-address-with-bkey\0".as_ptr().cast(),
is_bkey.into(),
);
}

llmod
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fn declare_raw_fn(

attributes::default_optimisation_attrs(cx.tcx.sess, llfn);
attributes::non_lazy_bind(cx.sess(), llfn);

llfn
}

Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use rustc_session::config::{build_configuration, build_session_options, to_crate
use rustc_session::config::{
rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes,
};
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
use rustc_session::config::{
Externs, OutputType, OutputTypes, SymbolManglingVersion, WasiExecModel,
BranchProtection, Externs, OutputType, OutputTypes, PAuthKey, PacRet, SymbolManglingVersion,
WasiExecModel,
};
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath;
use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
Expand Down Expand Up @@ -718,6 +719,10 @@ fn test_debugging_options_tracking_hash() {
tracked!(asm_comments, true);
tracked!(assume_incomplete_release, true);
tracked!(binary_dep_depinfo, true);
tracked!(
branch_protection,
BranchProtection { bti: true, pac_ret: Some(PacRet { leaf: true, key: PAuthKey::B }) }
);
tracked!(chalk, true);
tracked!(codegen_backend, Some("abc".to_string()));
tracked!(crate_attr, vec!["abc".to_string()]);
Expand Down
31 changes: 28 additions & 3 deletions compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,30 @@ impl Passes {
}
}

#[derive(Clone, Copy, Hash, Debug, PartialEq)]
pub enum PAuthKey {
A,
B,
}

#[derive(Clone, Copy, Hash, Debug, PartialEq)]
pub struct PacRet {
pub leaf: bool,
pub key: PAuthKey,
}

#[derive(Clone, Copy, Hash, Debug, PartialEq)]
pub struct BranchProtection {
pub bti: bool,
pub pac_ret: Option<PacRet>,
}

impl Default for BranchProtection {
fn default() -> Self {
BranchProtection { bti: false, pac_ret: None }
}
}

pub const fn default_lib_output() -> CrateType {
CrateType::Rlib
}
Expand Down Expand Up @@ -2487,9 +2511,9 @@ impl PpMode {
crate mod dep_tracking {
use super::LdImpl;
use super::{
CFGuard, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, LinkerPluginLto,
LocationDetail, LtoCli, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm,
SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths,
BranchProtection, CFGuard, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage,
LinkerPluginLto, LocationDetail, LtoCli, OptLevel, OutputType, OutputTypes, Passes,
SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths,
};
use crate::lint;
use crate::options::WasiExecModel;
Expand Down Expand Up @@ -2583,6 +2607,7 @@ crate mod dep_tracking {
OutputType,
RealFileName,
LocationDetail,
BranchProtection,
);

impl<T1, T2> DepTrackingHash for (T1, T2)
Expand Down
30 changes: 30 additions & 0 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ mod desc {
pub const parse_gcc_ld: &str = "one of: no value, `lld`";
pub const parse_stack_protector: &str =
"one of (`none` (default), `basic`, `strong`, or `all`)";
pub const parse_branch_protection: &str =
"a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`";
}

mod parse {
Expand Down Expand Up @@ -929,6 +931,32 @@ mod parse {
}
true
}

crate fn parse_branch_protection(slot: &mut BranchProtection, v: Option<&str>) -> bool {
match v {
Some(s) => {
for opt in s.split(',') {
match opt {
"bti" => slot.bti = true,
"pac-ret" if slot.pac_ret.is_none() => {
slot.pac_ret = Some(PacRet { leaf: false, key: PAuthKey::A })
}
"leaf" => match slot.pac_ret.as_mut() {
Some(pac) => pac.leaf = true,
_ => return false,
},
"b-key" => match slot.pac_ret.as_mut() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should ideally work even if b-key comes before pac-ret in the list of options. Same for leaf.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are modifiers for pac-ret so to me it makes sense to put them after it. This is also what Clang requires (empirically). However, the main reason behind this is more practical: Making Rust flexible (i.e. treating these as an unordered set) is great, but we can't go back without breaking users' build scripts. Conversely, making Rust strict (like C compilers) makes it easier for us to match future changes to the C options, or to extend it in Rust-specific ways (currently unexplored).

We can of course change this to suit Rust's practices if you think that's more appropriate, but I thought I should explain why it was done this way. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with leaving this as an outstanding question for the tracking issue for this feature.

Some(pac) => pac.key = PAuthKey::B,
_ => return false,
},
_ => return false,
};
}
}
_ => return false,
}
true
}
}

options! {
Expand Down Expand Up @@ -1070,6 +1098,8 @@ options! {
(default: no)"),
borrowck: String = ("migrate".to_string(), parse_string, [UNTRACKED],
"select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"),
branch_protection: BranchProtection = (BranchProtection::default(), parse_branch_protection, [TRACKED],
"set options for branch target identification and pointer authentication on AArch64"),
cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED],
"the codegen unit partitioning strategy to use"),
chalk: bool = (false, parse_bool, [TRACKED],
Expand Down
18 changes: 18 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/branch-protection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# `branch-protection`

This option lets you enable branch authentication instructions on AArch64.
This option is ignored for non-AArch64 architectures.
It takes some combination of the following values, separated by a `,`.

- `pac-ret` - Enable pointer authentication for non-leaf functions.
- `leaf` - Enable pointer authentication for all functions, including leaf functions.
- `b-key` - Sign return addresses with key B, instead of the default key A.
- `bti` - Enable branch target identification.

`leaf` and `b-key` are only valid if `pac-ret` was previously specified.
For example, `-Z branch-protection=bti,pac-ret,leaf` is valid, but
`-Z branch-protection=bti,leaf,pac-ret` is not.

Rust's standard library does not ship with BTI or pointer authentication enabled by default.
In Cargo projects the standard library can be recompiled with pointer authentication using the nightly
[build-std](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std) feature.
22 changes: 22 additions & 0 deletions src/test/assembly/aarch64-pointer-auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Test that PAC instructions are emitted when branch-protection is specified.

// min-llvm-version: 10.0.1
// assembly-output: emit-asm
// compile-flags: --target aarch64-unknown-linux-gnu
// compile-flags: -Z branch-protection=pac-ret,leaf
// needs-llvm-components: aarch64

#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#![crate_type = "lib"]

#[lang = "sized"]
trait Sized {}

// CHECK: hint #25
// CHECK: hint #29
#[no_mangle]
pub fn test() -> u8 {
42
}
41 changes: 41 additions & 0 deletions src/test/codegen/branch-protection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Test that the correct module flags are emitted with different branch protection flags.

// revisions: bti pac-ret leaf b-key
// min-llvm-version: 12.0.0
// needs-llvm-components: aarch64
// [bti] compile-flags: -Z branch-protection=bti
// [pac-ret] compile-flags: -Z branch-protection=pac-ret
// [leaf] compile-flags: -Z branch-protection=pac-ret,leaf
// [b-key] compile-flags: -Z branch-protection=pac-ret,b-key
// compile-flags: --target aarch64-unknown-linux-gnu

#![crate_type = "lib"]
#![feature(no_core, lang_items)]
#![no_core]

#[lang="sized"]
trait Sized { }

// A basic test function.
pub fn test() {
}

// bti: !"branch-target-enforcement", i32 1
// bti: !"sign-return-address", i32 0
// bti: !"sign-return-address-all", i32 0
// bti: !"sign-return-address-with-bkey", i32 0

// pac-ret: !"branch-target-enforcement", i32 0
// pac-ret: !"sign-return-address", i32 1
// pac-ret: !"sign-return-address-all", i32 0
// pac-ret: !"sign-return-address-with-bkey", i32 0

// leaf: !"branch-target-enforcement", i32 0
// leaf: !"sign-return-address", i32 1
// leaf: !"sign-return-address-all", i32 1
// leaf: !"sign-return-address-with-bkey", i32 0

// b-key: !"branch-target-enforcement", i32 0
// b-key: !"sign-return-address", i32 1
// b-key: !"sign-return-address-all", i32 0
// b-key: !"sign-return-address-with-bkey", i32 1
14 changes: 14 additions & 0 deletions src/test/run-make-fulldeps/pointer-auth-link-with-c/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-include ../tools.mk

# only-aarch64

all:
$(COMPILE_OBJ) $(TMPDIR)/test.o test.c
$(AR) rcs $(TMPDIR)/libtest.a $(TMPDIR)/test.o
$(RUSTC) -Z branch-protection=bti,pac-ret,leaf test.rs
$(call RUN,test)

$(COMPILE_OBJ) $(TMPDIR)/test.o test.c -mbranch-protection=bti+pac-ret+leaf
$(AR) rcs $(TMPDIR)/libtest.a $(TMPDIR)/test.o
$(RUSTC) -Z branch-protection=bti,pac-ret,leaf test.rs
$(call RUN,test)
1 change: 1 addition & 0 deletions src/test/run-make-fulldeps/pointer-auth-link-with-c/test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int foo() { return 0; }
8 changes: 8 additions & 0 deletions src/test/run-make-fulldeps/pointer-auth-link-with-c/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[link(name = "test")]
extern "C" {
fn foo() -> i32;
}

fn main() {
unsafe {foo();}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// compile-flags: -Z branch-protection=leaf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
error: incorrect value `leaf` for debugging option `branch-protection` - a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf` was expected