Skip to content

Commit a9c8a37

Browse files
committed
Add pauth attributes and first IR tests
The set of supported attributes is: function * "aarch64-jump-table-hardening" * "ptrauth-auth-traps" * "ptrauth-calls" * "ptrauth-indirect-gotos" * "ptrauth-returns" module * "ptrauth-elf-got" * "ptrauth-sign-personality"
1 parent 0faa8ac commit a9c8a37

7 files changed

Lines changed: 246 additions & 4 deletions

File tree

compiler/rustc_codegen_llvm/src/attributes.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ use rustc_middle::ty::{self, TyCtxt};
99
use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet};
1010
use rustc_span::sym;
1111
use rustc_symbol_mangling::mangle_internal_symbol;
12-
use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector};
12+
use rustc_target::spec::{Arch, Env, FramePointer, SanitizerSet, StackProbeType, StackProtector};
1313
use smallvec::SmallVec;
1414

15+
use crate::common::pauth_fn_attrs;
1516
use crate::context::SimpleCx;
1617
use crate::errors::{PackedStackBackchainNeedsSoftfloat, SanitizerMemtagRequiresMte};
1718
use crate::llvm::AttributePlace::Function;
@@ -605,6 +606,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>(
605606
}
606607
}
607608

609+
if sess.target.env == Env::Pauthtest {
610+
for &ptrauth_attr in pauth_fn_attrs() {
611+
to_add.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr));
612+
}
613+
}
614+
608615
to_add.extend(target_features_attr(cx, tcx, function_features));
609616

610617
attributes::apply_to_llfn(llfn, Function, &to_add);

compiler/rustc_codegen_llvm/src/base.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,14 @@ pub(crate) fn compile_codegen_unit(
125125
if let Some(entry) =
126126
maybe_create_entry_wrapper::<Builder<'_, '_, '_>>(&cx, cx.codegen_unit)
127127
{
128-
let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default());
128+
let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default());
129+
// For pauthtest make sure that the ptrauth-* attributes are also attached to the
130+
// entry wrapper.
131+
if cx.sess().target.env == Env::Pauthtest {
132+
for &ptrauth_attr in pauth_fn_attrs() {
133+
attrs.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr));
134+
}
135+
}
129136
attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs);
130137
}
131138

@@ -142,6 +149,19 @@ pub(crate) fn compile_codegen_unit(
142149
cx.add_objc_module_flags();
143150
}
144151

152+
if cx.sess().target.env == Env::Pauthtest {
153+
// FIXME(jchlanda): In LLVM/Clang, there also `aarch64-elf-pauthabi-platform` and
154+
// `aarch64-elf-pauthabi-version` module flags, ending up in PAuth core info
155+
// emitted in a special section in resulting ELF. This is used by static and
156+
// dynamic linker to check binary compatibility. By default, absence of that info
157+
// is treated as being compatible with anything. While not needed now, it will
158+
// become relevant when C++ interop is implemented.
159+
if cx.sess().opts.unstable_opts.pauth_enable_elf_got {
160+
cx.add_ptrauth_elf_got_flag();
161+
}
162+
cx.add_ptrauth_sign_personality_flag();
163+
}
164+
145165
// Finalize code coverage by injecting the coverage map. Note, the coverage map will
146166
// also be added to the `llvm.compiler.used` variable, created next.
147167
if cx.sess().instrument_coverage() {

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,24 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
688688
}
689689
}
690690

691+
pub(crate) fn add_ptrauth_elf_got_flag(&self) {
692+
llvm::add_module_flag_u32(
693+
self.llmod,
694+
llvm::ModuleFlagMergeBehavior::Max,
695+
"ptrauth-elf-got",
696+
1,
697+
);
698+
}
699+
700+
pub(crate) fn add_ptrauth_sign_personality_flag(&self) {
701+
llvm::add_module_flag_u32(
702+
self.llmod,
703+
llvm::ModuleFlagMergeBehavior::Max,
704+
"ptrauth-sign-personality",
705+
1,
706+
);
707+
}
708+
691709
// We do our best here to match what Clang does when compiling Objective-C natively.
692710
// See Clang's `CGObjCCommonMac::EmitImageInfo`:
693711
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_session::config::CrateType;
2323
use rustc_span::{Span, Symbol, sym};
2424
use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate};
2525
use rustc_target::callconv::PassMode;
26-
use rustc_target::spec::Os;
26+
use rustc_target::spec::{Env, Os};
2727
use tracing::debug;
2828

2929
use crate::abi::FnAbiLlvmExt;
@@ -32,12 +32,13 @@ use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call};
3232
use crate::builder::gpu_offload::{
3333
OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload,
3434
};
35+
use crate::common::pauth_fn_attrs;
3536
use crate::context::CodegenCx;
3637
use crate::declare::declare_raw_fn;
3738
use crate::errors::{
3839
AutoDiffWithoutEnable, AutoDiffWithoutLto, OffloadWithoutEnable, OffloadWithoutFatLTO,
3940
};
40-
use crate::llvm::{self, Type, Value};
41+
use crate::llvm::{self, Attribute, AttributePlace, Type, Value};
4142
use crate::type_of::LayoutLlvmExt;
4243
use crate::va_arg::emit_va_arg;
4344

@@ -1312,6 +1313,13 @@ fn get_rust_try_fn<'a, 'll, 'tcx>(
13121313
ExternAbi::Rust,
13131314
));
13141315
let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
1316+
if cx.sess().target.env == Env::Pauthtest {
1317+
let attrs: Vec<&Attribute> =
1318+
pauth_fn_attrs().iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect();
1319+
let (_ty, rust_try_fn) = rust_try;
1320+
crate::attributes::apply_to_llfn(rust_try_fn, AttributePlace::Function, &attrs);
1321+
}
1322+
13151323
cx.rust_try_fn.set(Some(rust_try));
13161324
rust_try
13171325
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ only-aarch64-unknown-linux-pauthtest
2+
//@ compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0
3+
// Make sure that compiler generated functions (main wrapper and __rust_try) also have ptrauth
4+
// attributes set correctly. Rustc only generates __rust_try at O0, so use that opt level for the
5+
// test.
6+
7+
//@ needs-llvm-components: aarch64
8+
9+
use std::panic;
10+
11+
// CHECK: define {{.*}} @__rust_try{{.*}} [[ATTR_TRY:#[0-9]+]]
12+
// CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]]
13+
14+
// CHECK: attributes [[ATTR_TRY]] = { {{.*}}"aarch64-jump-table-hardening"
15+
// CHECK-DAG: "ptrauth-auth-traps"
16+
// CHECK-DAG: "ptrauth-calls"
17+
// CHECK-DAG: "ptrauth-indirect-gotos"
18+
// CHECK-DAG: "ptrauth-returns"
19+
20+
// CHECK: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening"
21+
// CHECK-DAG: "ptrauth-auth-traps"
22+
// CHECK-DAG: "ptrauth-calls"
23+
// CHECK-DAG: "ptrauth-indirect-gotos"
24+
// CHECK-DAG: "ptrauth-returns"
25+
fn main() {
26+
let _ = panic::catch_unwind(|| {
27+
panic!("BOOM");
28+
});
29+
}
30+
31+
// CHECK: !{{[0-9]+}} = !{i32 7, !"ptrauth-sign-personality", i32 1}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// ignore-tidy-linelength
2+
//@ only-aarch64-unknown-linux-pauthtest
3+
//@ revisions: O0_PAUTH O3_PAUTH
4+
5+
//@ [O0_PAUTH] needs-llvm-components: aarch64
6+
//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0
7+
//@ [O3_PAUTH] needs-llvm-components: aarch64
8+
//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3
9+
10+
// Make sure that direct extern "C" calls are not handled by pointer authentication operand bundle
11+
// logic.
12+
use std::ffi::c_void;
13+
use std::hint::black_box;
14+
15+
extern "C" {
16+
fn rand() -> i32;
17+
fn add(a: i32, b: i32) -> i32;
18+
fn sub(a: i32, b: i32) -> i32;
19+
20+
// Corresponds to: void *woof;
21+
static mut woof: *mut c_void;
22+
fn direct_function_taking_void_arg(data: *mut c_void);
23+
fn direct_no_arg();
24+
fn direct_function_taking_fp_arg(func: unsafe extern "C" fn());
25+
}
26+
27+
type CFnPtr = unsafe extern "C" fn(i32, i32) -> i32;
28+
29+
// CHECK-LABE: test_indirect_call
30+
#[inline(never)]
31+
fn test_indirect_call() {
32+
let fp_add: CFnPtr = black_box(add);
33+
let fp_sub: CFnPtr = black_box(sub);
34+
35+
unsafe {
36+
// O0_PAUTH: call {{.*}}i32 %fp_add({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
37+
// O3_PAUTH: call {{.*}}i32 %fp_add({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
38+
let _id1 = fp_add(7, 4);
39+
// O0_PAUTH: call {{.*}}i32 %fp_sub({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
40+
// O3_PAUTH: call {{.*}}i32 %fp_sub({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
41+
let _id2 = fp_sub(10, 6);
42+
}
43+
44+
// Also test calling via conditional pointer
45+
unsafe {
46+
// O0_PAUTH: call {{.*}}i32 ptrauth (ptr @rand, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
47+
// O3_PAUTH: call {{.*}}i32 @rand() #
48+
let use_add = rand() % 2 == 0;
49+
// O0_PAUTH: store ptr ptrauth (ptr @sub, i32 0), ptr %[[FP_O0:[a-zA-Z0-9_.]+]]
50+
// O0_PAUTH: store ptr ptrauth (ptr @add, i32 0), ptr %[[FP_O0]]{{.*}}
51+
// O0_PAUTH: %[[LOAD_FP_O0:[a-zA-Z0-9_.]+]] = load ptr, ptr %[[FP_O0]]{{.*}}
52+
// O3_PAUTH: %[[FP_O3:.*]] = select i1 %{{.*}}, ptr ptrauth (ptr @add, i32 0), ptr ptrauth (ptr @sub, i32 0)
53+
let fp: CFnPtr = if use_add { add } else { sub };
54+
// O0_PAUTH: call i32 %[[LOAD_FP_O0]](i32 1, i32 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
55+
// O3_PAUTH: call {{.*}}i32 %[[FP_O3]](i32 noundef 1, i32 noundef 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
56+
let _id3 = fp(1, 2);
57+
}
58+
59+
unsafe {
60+
direct_function_taking_fp_arg(direct_no_arg);
61+
}
62+
}
63+
64+
// CHECK-LABE: test_direct_call
65+
#[inline(never)]
66+
fn test_direct_call() {
67+
unsafe {
68+
// O0_PAUTH: call {{.*}}i32 ptrauth (ptr @add, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
69+
// O3_PAUTH: call {{.*}}i32 @add(i32 {{.*}}2, i32 {{.*}}3) #
70+
let _d1 = add(2, 3);
71+
// O0_PAUTH: call {{.*}}i32 ptrauth (ptr @sub, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
72+
// O3_PAUTH: call {{.*}}i32 @sub(i32 {{.*}}5, i32 {{.*}}1) #
73+
let _d2 = sub(5, 1);
74+
75+
// O0_PAUTH: call {{.*}}void ptrauth (ptr @direct_function_taking_void_arg, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ]
76+
// O3_PAUTH: {{(tail )?}}call void @direct_function_taking_void_arg(ptr noundef %{{.*}}) #
77+
direct_function_taking_void_arg(woof);
78+
}
79+
}
80+
81+
fn main() {
82+
test_indirect_call();
83+
test_direct_call();
84+
}
85+
86+
// O0_PAUTH: !{{[0-9]+}} = !{i32 7, !"ptrauth-sign-personality", i32 1}
87+
// O3_PAUTH: !{{[0-9]+}} = !{i32 7, !"ptrauth-sign-personality", i32 1}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// ignore-tidy-linelength
2+
//@ only-aarch64-unknown-linux-pauthtest
3+
//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ELF-GOT O3_PAUTH-ELF-GOT O0_NO_PAUTH O3_NO_PAUTH
4+
5+
//@ [O0_PAUTH] needs-llvm-components: aarch64
6+
//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0
7+
//@ [O3_PAUTH] needs-llvm-components: aarch64
8+
//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3
9+
//@ [O0_PAUTH-ELF-GOT] needs-llvm-components: aarch64
10+
//@ [O0_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Z pauth_enable_elf_got
11+
//@ [O3_PAUTH-ELF-GOT] needs-llvm-components: aarch64
12+
//@ [O3_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Z pauth_enable_elf_got
13+
//@ [O0_NO_PAUTH] needs-llvm-components: aarch64
14+
//@ [O0_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=0
15+
//@ [O3_NO_PAUTH] needs-llvm-components: aarch64
16+
//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3
17+
18+
use std::hint::black_box;
19+
20+
type FnPtr = unsafe extern "C" fn(i32, i32) -> i32;
21+
22+
// O0_NO_PAUTH-NOT: "ptrauth"(i32
23+
// O3_NO_PAUTH-NOT: "ptrauth"(i32
24+
25+
// O0_PAUTH: define {{.*}}pauth_extern_c4main
26+
// O3_PAUTH: define {{.*}}pauth_extern_c4main
27+
fn main() {
28+
// O0_PAUTH: ptr ptrauth (ptr @add_from_c, i32 0)
29+
// O3_PAUTH: ptr ptrauth (ptr @add_from_c, i32 0)
30+
let add_ptr: FnPtr = black_box(add_from_c);
31+
// O0_PAUTH: call i32 @{{.*}}pauth_extern_c7call_it{{.*}}(ptr {{.*}})
32+
let _sum = call_it(add_ptr, 5, 7);
33+
assert!(12 == _sum);
34+
}
35+
36+
// O0_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O0_1:[0-9]+]]
37+
// O3_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O3_1:[0-9]+]]
38+
#[inline(never)]
39+
fn call_it(fn_ptr: FnPtr, arg_1: i32, arg_2: i32) -> i32 {
40+
// O0_PAUTH: call i32 %fn_ptr(i32 %arg_1, i32 %arg_2) {{.*}} [ "ptrauth"(i32 0, i64 0) ]
41+
// O3_PAUTH: call {{.*}} i32 %fn_ptr(i32 {{.*}}, i32 {{.*}}) {{.*}} [ "ptrauth"(i32 0, i64 0) ]
42+
unsafe { fn_ptr(arg_1, arg_2) }
43+
}
44+
45+
extern "C" {
46+
fn add_from_c(a: i32, b: i32) -> i32;
47+
}
48+
49+
// O0_PAUTH-CHECK: attributes #[[ATTR_O0_1]] = { {{.*}}"aarch64-jump-table-hardening"
50+
// O0_PAUTH-CHECK-DAG: "ptrauth-auth-traps"
51+
// O0_PAUTH-CHECK-DAG: "ptrauth-calls"
52+
// O0_PAUTH-CHECK-DAG: "ptrauth-indirect-gotos"
53+
// O0_PAUTH-CHECK-DAG: "ptrauth-returns"
54+
55+
// O3_PAUTH-CHECK: attributes #[[ATTR_O3_1]] = { {{.*}}"aarch64-jump-table-hardening"
56+
// O3_PAUTH-CHECK-DAG: "ptrauth-auth-traps"
57+
// O3_PAUTH-CHECK-DAG: "ptrauth-calls"
58+
// O3_PAUTH-CHECK-DAG: "ptrauth-indirect-gotos"
59+
// O3_PAUTH-CHECK-DAG: "ptrauth-returns"
60+
61+
// O0_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-elf-got", i32 1}
62+
// O0_PAUTH-NOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-elf-got", i32 1}
63+
// O0_PAUTH: !{{[0-9]+}} = !{i32 7, !"ptrauth-sign-personality", i32 1}
64+
// O3_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-elf-got", i32 1}
65+
// O3_PAUTH-NOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-elf-got", i32 1}
66+
// O3_PAUTH: !{{[0-9]+}} = !{i32 7, !"ptrauth-sign-personality", i32 1}
67+
68+
// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-elf-got", i32 1}
69+
// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-sign-personality", i32 1}
70+
// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-elf-got", i32 1}
71+
// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 7, !"ptrauth-sign-personality", i32 1}

0 commit comments

Comments
 (0)