Skip to content
Open
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
31 changes: 21 additions & 10 deletions compiler/rustc_codegen_llvm/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,19 @@ use crate::llvm::{
self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, TypeKind, Value, const_ptr_auth,
};

/// Return true if the ABI of an `instance` is supported by pointer authentication (`extern "C"` and
/// `extern "system"`).
pub(crate) fn is_ptrauth_signed_fn<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool {
let def_id = instance.def_id();
matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn)
&& matches!(
tcx.fn_sig(def_id).skip_binder().abi(),
ExternAbi::C { .. } | ExternAbi::System { .. }
)
}

pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>(
cx: &CodegenCx<'ll, '_>,
cx: &CodegenCx<'ll, 'tcx>,
instance: Instance<'tcx>,
llfn: &'ll llvm::Value,
schema: &PointerAuthSchema,
Expand All @@ -35,16 +46,10 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>(
return llfn;
}

// Only free functions or methods
let def_id = instance.def_id();
if !matches!(cx.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
return llfn;
}
// Only C ABI
let abi = cx.tcx.fn_sig(def_id).skip_binder().abi();
if !matches!(abi, ExternAbi::C { .. } | ExternAbi::System { .. }) {
if !is_ptrauth_signed_fn(cx.tcx, instance) {
return llfn;
}
let def_id = instance.def_id();
// Ignore LLVM intrinsics
if llvm::get_value_name(llfn).starts_with(b"llvm.") {
return llfn;
Expand All @@ -54,8 +59,14 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>(
}

let addr_diversity = match schema.is_address_discriminated {
PointerAuthAddressDiscriminator::HardwareAddress(true) => Some(llfn),
// Address discrimination blends in the address of the slot holding the pointer, that
// feature is unsupported. Treat it as a bug.
PointerAuthAddressDiscriminator::HardwareAddress(true) => {
bug!("address-discriminated function pointer schemas are not supported")
}
PointerAuthAddressDiscriminator::HardwareAddress(false) => None,
// init/fini entries use a synthetic placeholder. If set, the loader will blend in the
// run-time address.
PointerAuthAddressDiscriminator::Synthetic(val) => {
let llval = cx.const_u64(val);
let llty = cx.val_ty(llfn);
Expand Down
41 changes: 37 additions & 4 deletions compiler/rustc_codegen_llvm/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
Allocation, ConstAllocation, ErrorHandled, InitChunk, Pointer, Scalar as InterpScalar,
read_target_uint,
Allocation, ConstAllocation, ErrorHandled, GlobalAlloc, InitChunk, Pointer,
Scalar as InterpScalar, read_target_uint,
};
use rustc_middle::mono::MonoItem;
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
Expand All @@ -19,8 +19,8 @@ use rustc_span::{Symbol, bug, span_bug};
use rustc_target::spec::Arch;
use tracing::{debug, instrument, trace};

use crate::common::CodegenCx;
use crate::diagnostics::SymbolAlreadyDefined;
use crate::common::{CodegenCx, is_ptrauth_signed_fn};
use crate::diagnostics::{PtrauthUnsignedInitFiniEntry, SymbolAlreadyDefined};
use crate::llvm::{self, Type, Value, const_ptr_auth};
use crate::{base, debuginfo};

Expand Down Expand Up @@ -173,9 +173,42 @@ fn codegen_static_initializer<'ll, 'tcx>(
}
})
.unwrap_or(IsInitOrFini::No);
if let IsInitOrFini::Yes = is_in_init_fini {
check_init_fini_entries_are_signed(cx, def_id, alloc.inner());
}
Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc))
}

/// For pointer authentication the loader authenticates every entry of an init/fini array with the
/// init/fini schema, but only functions whose ABI is supported (see `is_ptrauth_signed_fn`) can be
/// signed.
/// Reject non supported entries instead of emitting entries that fail authentication when the
/// loader runs them.
fn check_init_fini_entries_are_signed<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
def_id: DefId,
alloc: &Allocation,
) {
if cx.sess().pointer_authentication_init_fini().is_none() {
return;
}
let section = cx.tcx.codegen_fn_attrs(def_id).link_section.map(|s| s.to_string());
let mut reported = Vec::new();
for &(_, prov) in alloc.provenance().ptrs().iter() {
if let GlobalAlloc::Function { instance, .. } = cx.tcx.global_alloc(prov.alloc_id())
&& !is_ptrauth_signed_fn(cx.tcx, instance)
&& !reported.contains(&instance)
{
reported.push(instance);
cx.tcx.dcx().emit_err(PtrauthUnsignedInitFiniEntry {
span: cx.tcx.def_span(def_id),
function: cx.tcx.def_path_str(instance.def_id()),
section: section.clone().unwrap_or_default(),
});
}
}
}

fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {
// The target may require greater alignment for globals than the type does.
// Note: GCC and Clang also allow `__attribute__((aligned))` on variables,
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_codegen_llvm/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ pub(crate) struct SymbolAlreadyDefined<'a> {
#[diag("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`")]
pub(crate) struct SanitizerMemtagRequiresMte;

#[derive(Diagnostic)]
#[diag("`{$function}` is placed in `{$section}`, but pointer authentication does not sign its ABI")]
#[note(
"the loader authenticates every entry of `.init_array` and `.fini_array` with the init/fini signing schema, so an unsigned entry fails authentication when the loader runs it"
)]
#[help("declare `{$function}` as `extern \"C\"`")]
pub(crate) struct PtrauthUnsignedInitFiniEntry {
#[primary_span]
pub span: Span,
pub function: String,
pub section: String,
}

pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);

impl Diagnostic<'_> for ParseTargetMachineConfig<'_> {
Expand Down
38 changes: 19 additions & 19 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ pub enum PointerAuthDiscrimination {
pub enum PointerAuthAddressDiscriminator {
/// Enable/disable hardware address discrimination.
HardwareAddress(bool),
/// Use a synthetic value. For instance init/fini entries can not the address of the arrays,
/// they must use a synthetic value of `1`.
/// Use a synthetic value. Address-discriminated init/fini entries use a placeholder value of
/// `1`, (LLVM's `ConstantPtrAuth::AddrDiscriminator_CtorsDtors`).
Synthetic(u64),
}

Expand Down Expand Up @@ -158,6 +158,9 @@ pub struct PointerAuthConfig {
pub function_pointers: Option<PointerAuthSchema>,
/// The ABI for function addresses in .init_array and .fini_array
pub init_fini: Option<PointerAuthSchema>,
/// Should init/fini entries be address discriminated? Kept apart from `init_fini`, as in Clang:
/// it is recorded in the PAuth ABI version even when init/fini signing is disabled.
pub init_fini_address_discrimination: bool,
/// Use of pointer authentication intrinsics.
pub intrinsics: bool,
/// The following are used only for compatibility with C++ and control over generated abi
Expand All @@ -177,6 +180,7 @@ impl PointerAuthConfig {
aarch64_jump_table_hardening: true,
function_pointers: Some(PointerAuthSchema::function_pointers_default(target)),
init_fini: Some(PointerAuthSchema::init_fini_default(target)),
init_fini_address_discrimination: true,
intrinsics: true,
typeinfo_vt_ptr_discrimination: true,
vt_ptr_addr_discrimination: true,
Expand Down Expand Up @@ -215,13 +219,7 @@ impl PointerAuthConfig {
| (u32::from(self.vt_ptr_addr_discrimination) << VT_PTR_ADDR_DISCR)
| (u32::from(self.vt_ptr_type_discrimination) << VT_PTR_TYPE_DISCR)
| (u32::from(self.init_fini.is_some()) << INIT_FINI)
| (u32::from(self.init_fini.as_ref().is_some_and(|schema| {
matches!(
schema.is_address_discriminated,
PointerAuthAddressDiscriminator::HardwareAddress(true)
| PointerAuthAddressDiscriminator::Synthetic(_)
)
})) << INIT_FINI_ADDR_DISC)
| (u32::from(self.init_fini_address_discrimination) << INIT_FINI_ADDR_DISC)
| (u32::from(self.elf_got) << GOT)
| (u32::from(self.indirect_gotos) << GOTOS)
| (u32::from(self.typeinfo_vt_ptr_discrimination) << TYPEINFO_VT_PTR_DISCR);
Expand Down Expand Up @@ -275,16 +273,7 @@ impl PointerAuthConfig {
}
}
PointerAuthOption::InitFiniAddressDiscrimination => {
if *enabled {
let schema = cfg
.init_fini
.get_or_insert_with(|| PointerAuthSchema::init_fini_default(target));
schema.is_address_discriminated =
PointerAuthAddressDiscriminator::HardwareAddress(true);
} else if let Some(schema) = &mut cfg.init_fini {
schema.is_address_discriminated =
PointerAuthAddressDiscriminator::Synthetic(1);
}
cfg.init_fini_address_discrimination = *enabled
}

PointerAuthOption::Intrinsics => cfg.intrinsics = *enabled,
Expand All @@ -296,6 +285,17 @@ impl PointerAuthConfig {
}
}

// With address discrimination an entry carries the placeholder address discriminator `1`
// (see `PointerAuthAddressDiscriminator::Synthetic`), and without it no address
// discriminator at all.
if let Some(schema) = &mut cfg.init_fini {
schema.is_address_discriminated = if cfg.init_fini_address_discrimination {
PointerAuthAddressDiscriminator::Synthetic(1)
} else {
PointerAuthAddressDiscriminator::HardwareAddress(false)
};
}

Some(cfg)
}
pub fn fn_attrs(&self) -> Vec<&'static str> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,15 @@ disable a feature, where `<name>` is one of:
* `function-pointer-type-discrimination` - enable type discrimination for C
function pointers
* `indirect-gotos` - enable signing and authentication of indirect goto targets
* `init-fini` - enable signing of function pointers stored in init/fini arrays
* `init-fini` - enable signing of function pointers stored in init/fini arrays.
Only `extern "C"` and `extern "system"` functions are signed, so a static in
an `.init_array` or `.fini_array` section that points to a function of any
other ABI is rejected
* `init-fini-address-discrimination` - enable address discrimination for
init/fini array entries
init/fini array entries (on by default). As in Clang, an entry is signed
with address diversity, so the loader blends in the address of the entry.
Also as in Clang, this option does not enable `init-fini`, and it sets its
bit of the PAuth ABI version even when `init-fini` is disabled
* `intrinsics` - enable pointer authentication intrinsics
* `return-addresses` - enable signing and authentication of return addresses
* `typeinfo-vt-ptr-discrimination` - enable type/address discrimination for
Expand Down Expand Up @@ -458,6 +464,7 @@ This target can be tested as normal with `x.py`.
The following categories are supported (all present in tree):
* Assembly tests
* pauth-basic.rs
* pauth-init-fini.rs
* LLVM IR/codegen tests
* pauth-extern-c.rs
* pauth-extern-c-direct-indirect-call.rs
Expand All @@ -473,6 +480,7 @@ The following categories are supported (all present in tree):
* enable_pointer_authentication_validation.rs
* invalid_target_pointer_authentication.rs
* type_discrimination_not_supported_pointer_authentication.rs
* init-fini-unsigned-abi.rs
* incompatible_pauth.rs
* Unit tests for siphash function:
* compiler/rustc_middle/src/ptrauth/llvm_siphash/tests.rs
Expand All @@ -489,6 +497,7 @@ x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \
codegen-llvm codegen-units coverage crashes incremental library mir-opt \
run-make rust_middle ui ui-fulldeps \
tests/assembly-llvm/pauth-basic.rs \
tests/assembly-llvm/pauth-init-fini.rs \
tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs \
tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs \
tests/codegen-llvm/pauth/pauth-extern-c.rs \
Expand All @@ -502,6 +511,7 @@ x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \
tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs \
tests/ui/pointer_authentication/invalid_target_pointer_authentication.rs \
tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs \
tests/ui/pointer_authentication/init-fini-unsigned-abi.rs \
tests/ui/target_modifiers/incompatible_pauth.rs
```

Expand Down
29 changes: 29 additions & 0 deletions tests/assembly-llvm/pauth-init-fini.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// ignore-tidy-file-linelength
//@ add-minicore
//@ assembly-output: emit-asm
//@ revisions: ADDR_DISC NO_ADDR_DISC
//@ [ADDR_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest
//@ [ADDR_DISC] needs-llvm-components: aarch64
//@ [NO_ADDR_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-init-fini-address-discrimination
//@ [NO_ADDR_DISC] needs-llvm-components: aarch64

// Init/fini entries are signed with the init/fini discriminator (55764). By default they are also
// address-discriminated, which LLVM emits as `addr` in the AUTH relocation: the loader blends in
// the address of the entry.
// `-init-fini-address-discrimination` turns that off.

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

extern crate minicore;

extern "C" fn init_fn() {}

// CHECK: .section .init_array.90
// ADDR_DISC: .xword {{.*}}init_fn@AUTH(ia,55764,addr)
// NO_ADDR_DISC: .xword {{.*}}init_fn@AUTH(ia,55764){{$}}
#[used]
#[link_section = ".init_array.90"]
static GLOBAL_INIT: extern "C" fn() = init_fn;
19 changes: 18 additions & 1 deletion tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ignore-tidy-file-linelength
//@ revisions: DEFAULT ALL DISABLE_JUMP DISABLE_AUTH_TRAPS DISABLE_CALLS DISABLE_INDIRCT_GOTOS DISABLE_RETURNS DISABLE_INTRINSICS DISABLE_TYPEINFO DISABLE_VT_PTR_ADDR DISABLE_VT_PTR_TYPE NONE
//@ revisions: DEFAULT ALL DISABLE_JUMP DISABLE_AUTH_TRAPS DISABLE_CALLS DISABLE_INDIRCT_GOTOS DISABLE_RETURNS DISABLE_INTRINSICS DISABLE_TYPEINFO DISABLE_VT_PTR_ADDR DISABLE_VT_PTR_TYPE DISABLE_INIT_FINI_ADDR DISABLE_INIT_FINI INIT_FINI_ADDR_ONLY NONE

//@ add-minicore
#![crate_type = "lib"]
Expand Down Expand Up @@ -31,6 +31,12 @@ extern crate minicore;
//@[DISABLE_VT_PTR_ADDR] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-vt-ptr-addr-discrimination
//@[DISABLE_VT_PTR_TYPE] needs-llvm-components: aarch64
//@[DISABLE_VT_PTR_TYPE] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-vt-ptr-type-discrimination
//@[DISABLE_INIT_FINI_ADDR] needs-llvm-components: aarch64
//@[DISABLE_INIT_FINI_ADDR] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-init-fini-address-discrimination
//@[DISABLE_INIT_FINI] needs-llvm-components: aarch64
//@[DISABLE_INIT_FINI] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-init-fini
//@[INIT_FINI_ADDR_ONLY] needs-llvm-components: aarch64
//@[INIT_FINI_ADDR_ONLY] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-init-fini,+init-fini-address-discrimination
//@[NONE] needs-llvm-components: aarch64
//@[NONE] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-aarch64-jump-table-hardening,-auth-traps,-calls,-indirect-gotos,-return-addresses,-init-fini,-init-fini-address-discrimination,-intrinsics,-typeinfo-vt-ptr-discrimination,-vt-ptr-addr-discrimination,-vt-ptr-type-discrimination

Expand Down Expand Up @@ -117,6 +123,17 @@ pub fn main() {}
// DISABLE_VT_PTR_TYPE: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458}
// DISABLE_VT_PTR_TYPE-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1759}

// Init/fini signing stays on; only its address discrimination bit (7) is cleared.
// DISABLE_INIT_FINI_ADDR: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458}
// DISABLE_INIT_FINI_ADDR-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1663}

// As in Clang, the address discrimination bit (7) follows its own option: disabling init/fini
// signing clears only bit 6, and enabling address discrimination does not enable init/fini signing.
// DISABLE_INIT_FINI: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458}
// DISABLE_INIT_FINI-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1727}
// INIT_FINI_ADDR_ONLY: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458}
// INIT_FINI_ADDR_ONLY-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1727}

// NONE-NOT: ptrauth-returns
// NONE-NOT: aarch64-jump-table-hardening
// NONE-NOT: ptrauth-auth-traps
Expand Down
Loading
Loading