Skip to content

Commit faed284

Browse files
committed
Add support for init/fini signing and correctly sign extern weak globals
Also add flag for ELF-GOT signing.
1 parent 289a741 commit faed284

5 files changed

Lines changed: 131 additions & 6 deletions

File tree

compiler/rustc_codegen_llvm/src/consts.rs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
4949
//
5050
// Statics have a guaranteed meaningful address so it's less clear that we want to do
5151
// something like this; it's also harder.
52-
if !is_static {
52+
if matches!(is_static, IsStatic::No) {
5353
assert!(alloc.len() != 0);
5454
}
5555
let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1);
@@ -120,14 +120,29 @@ pub(crate) fn const_alloc_to_llvm<'ll>(
120120
as u64;
121121

122122
let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx);
123-
124-
llvals.push(cx.scalar_to_backend(
123+
// Under pointer authentication, function pointers stored in init/fini arrays need special
124+
// handling.
125+
let pac_metadata = Some(
126+
if cx.sess().target.env == Env::Pauthtest && matches!(is_init_fini, IsInitOrFini::Yes) {
127+
PacMetadata {
128+
// Must correspond to ptrauth_key_init_fini_pointer from `ptrauth.h`.
129+
key: 0,
130+
// ptrauth_string_discriminator("init_fini")
131+
disc: 0xd9d4,
132+
addr_diversity: AddressDiversity::Synthetic(1),
133+
}
134+
} else {
135+
PacMetadata::default()
136+
},
137+
);
138+
llvals.push(cx.scalar_to_backend_with_pac(
125139
InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx),
126140
Scalar::Initialized {
127141
value: Primitive::Pointer(address_space),
128142
valid_range: WrappingRange::full(pointer_size),
129143
},
130144
cx.type_ptr_ext(address_space),
145+
pac_metadata,
131146
));
132147
next_offset = offset + pointer_size_bytes;
133148
}
@@ -152,7 +167,19 @@ fn codegen_static_initializer<'ll, 'tcx>(
152167
def_id: DefId,
153168
) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> {
154169
let alloc = cx.tcx.eval_static_initializer(def_id)?;
155-
Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc))
170+
let attrs = cx.tcx.codegen_fn_attrs(def_id);
171+
let is_in_init_fini: IsInitOrFini = attrs
172+
.link_section
173+
.map(|link_section| {
174+
let s = link_section.as_str();
175+
if s.starts_with(".init_array") || s.starts_with(".fini_array") {
176+
IsInitOrFini::Yes
177+
} else {
178+
IsInitOrFini::No
179+
}
180+
})
181+
.unwrap_or(IsInitOrFini::No);
182+
Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc))
156183
}
157184

158185
fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {
@@ -175,6 +202,7 @@ fn check_and_apply_linkage<'ll, 'tcx>(
175202
if let Some(linkage) = attrs.import_linkage {
176203
debug!("get_static: sym={} linkage={:?}", sym, linkage);
177204

205+
let mut should_sign = false;
178206
// Declare a symbol `foo`. If `foo` is an extern_weak symbol, we declare
179207
// an extern_weak function, otherwise a global with the desired linkage.
180208
let g1 = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) {
@@ -187,8 +215,13 @@ fn check_and_apply_linkage<'ll, 'tcx>(
187215
&& let ty::FnPtr(sig, header) = args.type_at(0).kind()
188216
{
189217
let fn_sig = sig.with(*header);
190-
191218
let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty());
219+
// Decide if the initializer needs to be signed
220+
if cx.sess().target.env == Env::Pauthtest
221+
&& matches!(fn_sig.abi(), ExternAbi::C { .. })
222+
{
223+
should_sign = true;
224+
}
192225
cx.declare_fn(sym, &fn_abi, None)
193226
} else {
194227
cx.declare_global(sym, cx.type_i8())
@@ -217,7 +250,24 @@ fn check_and_apply_linkage<'ll, 'tcx>(
217250
})
218251
});
219252
llvm::set_linkage(g2, llvm::Linkage::InternalLinkage);
220-
llvm::set_initializer(g2, g1);
253+
254+
// Sign the function pointer that is used to initialize the global
255+
let initializer = if should_sign {
256+
let key: u32 = 0;
257+
let discriminator: u64 = 0;
258+
259+
const_ptr_auth(
260+
cx.const_bitcast(g1, llty),
261+
key,
262+
discriminator,
263+
None, /* address_diversity */
264+
)
265+
} else {
266+
g1
267+
};
268+
269+
llvm::set_initializer(g2, initializer);
270+
221271
g2
222272
} else if cx.tcx.sess.target.arch == Arch::X86
223273
&& common::is_mingw_gnu_toolchain(&cx.tcx.sess.target)

compiler/rustc_session/src/options.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2564,6 +2564,7 @@ options! {
25642564
"use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"),
25652565
profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED],
25662566
"name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"),
2567+
ptrauth_elf_got: bool = (false, parse_bool, [TRACKED], "enable signing of ELF GOT entries"),
25672568
query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
25682569
"enable queries of the dependency graph for regression testing (default: no)"),
25692570
randomize_layout: bool = (false, parse_bool, [TRACKED],

tests/auxiliary/minicore.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,14 @@ impl Sync for () {}
280280

281281
impl<T, const N: usize> Sync for [T; N] {}
282282

283+
// Function pointers are treated as `Sync` to match real `core` behavior.
284+
//
285+
// We intentionally only cover the zero-argument form since tests using this minicore
286+
// are simplified to `fn() -> R` / `extern "C" fn() -> R` to avoid arity-specific
287+
// complexity. See: tests/codegen-llvm/pauth-extern-weak-global.rs for the use case.
288+
impl<R> Sync for fn() -> R {}
289+
impl<R> Sync for unsafe extern "C" fn() -> R {}
290+
283291
#[lang = "drop_in_place"]
284292
fn drop_in_place<T>(_: *mut T) {}
285293

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// ignore-tidy-linelength
2+
//@ only-aarch64-unknown-linux-pauthtest
3+
//@ revisions: O0_PAUTH O3_PAUTH O0_NO_PAUTH O3_NO_PAUTH
4+
//@ add-minicore
5+
6+
//@ [O0_PAUTH] needs-llvm-components: aarch64
7+
//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0
8+
//@ [O3_PAUTH] needs-llvm-components: aarch64
9+
//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3
10+
//@ [O0_NO_PAUTH] needs-llvm-components: aarch64
11+
//@ [O0_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=0
12+
//@ [O3_NO_PAUTH] needs-llvm-components: aarch64
13+
//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3
14+
15+
#![crate_type = "lib"]
16+
#![no_std]
17+
#![no_core]
18+
#![feature(no_core)]
19+
#![feature(linkage)]
20+
21+
extern crate minicore;
22+
use minicore::*;
23+
24+
// O0_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0)
25+
// O0_PAUTH: declare i64 @extern_weak_fn({{.*}})
26+
// O3_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0)
27+
// O3_PAUTH: declare {{.*}} i64 @extern_weak_fn({{.*}})
28+
//
29+
// O0_NO_PAUTH-NOT: ptr ptrauth
30+
// O3_NO_PAUTH-NOT: ptr ptrauth
31+
extern "C" {
32+
#[link_name = "extern_weak_fn"]
33+
#[linkage = "extern_weak"]
34+
fn extern_weak_fn() -> i64;
35+
}
36+
37+
#[used]
38+
static FUNCTION_PTR_DECL: unsafe extern "C" fn() -> i64 = extern_weak_fn;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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 init/fini metadata uses correct discriminator: 0xd9d4/55764
11+
12+
#![crate_type = "lib"]
13+
#![feature(linkage)]
14+
15+
// O0_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".init_array.90"
16+
// O3_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".init_array.90"
17+
#[used]
18+
#[link_section = ".init_array.90"]
19+
static GLOBAL_INIT: extern "C" fn() = init_fn;
20+
21+
// O0_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".fini_array.90"
22+
// O3_PAUTH: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr inttoptr (i64 1 to ptr)), section ".fini_array.90"
23+
#[used]
24+
#[link_section = ".fini_array.90"]
25+
static GLOBAL_FINI: extern "C" fn(i32) = fini_fn;
26+
27+
extern "C" fn init_fn() {}
28+
extern "C" fn fini_fn(_: i32) {}

0 commit comments

Comments
 (0)