From 8ed54f0efcfeedd41f02125e3cd797e767bb5bc4 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 7 May 2026 12:42:56 +0000 Subject: [PATCH 01/12] Pointer authentication config and user facing options This patch brings: * unified handling of pointer authentication options through: `-Zpointer-authentication`, with possible values: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses`. Toggled with `+`/`-`. * centralized handling of pointer authentication features. Session holds `pointer_auth_config: Option` * encapsulation of schema for function pointers and init/fini through `PointerAuthSchema`. This allowed for retiring of `PacMetadata`. * refactor enabling of pointer authentication in code, instead of relying on the target (`pauthtest`) use the session --- compiler/rustc_codegen_gcc/src/common.rs | 6 +- compiler/rustc_codegen_gcc/src/context.rs | 12 +- compiler/rustc_codegen_llvm/src/attributes.rs | 10 +- compiler/rustc_codegen_llvm/src/base.rs | 17 +- compiler/rustc_codegen_llvm/src/builder.rs | 4 +- compiler/rustc_codegen_llvm/src/common.rs | 34 +-- compiler/rustc_codegen_llvm/src/consts.rs | 37 ++-- compiler/rustc_codegen_llvm/src/context.rs | 17 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 10 +- compiler/rustc_codegen_ssa/src/base.rs | 13 +- compiler/rustc_codegen_ssa/src/common.rs | 5 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 29 ++- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 18 +- .../rustc_codegen_ssa/src/traits/consts.rs | 4 +- compiler/rustc_codegen_ssa/src/traits/misc.rs | 37 +--- compiler/rustc_codegen_ssa/src/traits/mod.rs | 2 +- compiler/rustc_session/src/config.rs | 34 ++- compiler/rustc_session/src/options.rs | 36 ++- compiler/rustc_session/src/session.rs | 206 +++++++++++++++++- .../aarch64-unknown-linux-pauthtest.md | 1 + .../pauth/pauth-attr-cli-flags.rs | 64 ++++++ .../pauth/pauth-attr-special-funcs.rs | 8 +- tests/codegen-llvm/pauth/pauth-extern-c.rs | 4 +- tests/codegen-llvm/pauth/pauth-init-fini.rs | 13 +- tests/ui/README.md | 4 + ...thentication_validation.all_unknown.stderr | 2 + ...ter_authentication_validation.empty.stderr | 2 + ...ter_authentication_validation.mixed.stderr | 2 + ...nable_pointer_authentication_validation.rs | 15 ++ ...uthentication_validation.unprefixed.stderr | 2 + 30 files changed, 518 insertions(+), 130 deletions(-) create mode 100644 tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs create mode 100644 tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr create mode 100644 tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr create mode 100644 tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr create mode 100644 tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs create mode 100644 tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 7450f11f3ce80..e73b8aab54d73 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -2,12 +2,12 @@ use gccjit::{LValue, RValue, ToRValue, Type}; use rustc_abi::Primitive::Pointer; use rustc_abi::{self as abi, HasDataLayout}; use rustc_codegen_ssa::traits::{ - BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, PacMetadata, - StaticCodegenMethods, + BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::LayoutOf; +use rustc_session::PointerAuthSchema; use crate::consts::const_alloc_to_gcc; use crate::context::{CodegenCx, new_array_type}; @@ -247,7 +247,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>, - _pac: Option, + _schema: Option<&PointerAuthSchema>, ) -> RValue<'gcc> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 3a6c415b38963..e20a1502a275d 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -5,9 +5,7 @@ use gccjit::{Block, CType, Context, Function, FunctionType, LValue, Location, RV use rustc_abi::{Align, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::errors as ssa_errors; -use rustc_codegen_ssa::traits::{ - BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods, PacMetadata, -}; +use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods}; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::interpret::Allocation; @@ -18,9 +16,9 @@ use rustc_middle::ty::layout::{ LayoutOfHelpers, }; use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt}; -use rustc_session::Session; #[cfg(feature = "master")] use rustc_session::config::DebugInfo; +use rustc_session::{PointerAuthSchema, Session}; use rustc_span::{DUMMY_SP, Span, Symbol, respan}; use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi}; @@ -400,7 +398,11 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>, _pac: Option) -> RValue<'gcc> { + fn get_fn_addr( + &self, + instance: Instance<'tcx>, + _schema: Option<&PointerAuthSchema>, + ) -> RValue<'gcc> { let func_name = self.tcx.symbol_name(instance).name; let func = if let Some(variable) = self.get_declared_value(func_name) { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 774ffa8df9342..1f00e47a89927 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -12,12 +12,9 @@ use rustc_session::config::{ }; use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::{ - Arch, FramePointer, LlvmAbi, SanitizerSet, StackProbeType, StackProtector, -}; +use rustc_target::spec::{Arch, FramePointer, SanitizerSet, StackProbeType, StackProtector}; use smallvec::SmallVec; -use crate::common::pauth_fn_attrs; use crate::context::SimpleCx; use crate::errors::{PackedStackBackchainNeedsSoftfloat, SanitizerMemtagRequiresMte}; use crate::llvm::AttributePlace::Function; @@ -668,8 +665,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } - if sess.target.llvm_abiname == LlvmAbi::Pauthtest { - for &ptrauth_attr in pauth_fn_attrs() { + if sess.pointer_authentication() { + let cfg = sess.pointer_auth_config.as_ref().unwrap(); + for ptrauth_attr in cfg.fn_attrs() { to_add.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); } } diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index bb4ae6e64560a..70f2ea4186d53 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -25,13 +25,12 @@ use rustc_middle::mono::Visibility; use rustc_middle::ty::TyCtxt; use rustc_session::config::{DebugInfo, Offload}; use rustc_span::Symbol; -use rustc_target::spec::{LlvmAbi, SanitizerSet}; +use rustc_target::spec::SanitizerSet; use super::ModuleLlvm; use crate::attributes; use crate::builder::Builder; use crate::builder::gpu_offload::OffloadGlobals; -use crate::common::pauth_fn_attrs; use crate::context::CodegenCx; use crate::llvm::{self, Value}; @@ -131,8 +130,9 @@ pub(crate) fn compile_codegen_unit( // FIXME(jchlanda) If it ever becomes necessary to ensure that all compiler // generated functions receive the ptrauth-* attributes, `declare_fn` or // `declare_raw_fn` could be used to provide those. - if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { - for &ptrauth_attr in pauth_fn_attrs() { + if cx.sess().pointer_authentication() { + let cfg = cx.sess().pointer_auth_config.as_ref().unwrap(); + for ptrauth_attr in cfg.fn_attrs() { attrs.push(llvm::CreateAttrString(cx.llcx, ptrauth_attr)); } } @@ -152,7 +152,7 @@ pub(crate) fn compile_codegen_unit( cx.add_objc_module_flags(); } - if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { + if cx.sess().pointer_authentication() { // FIXME(jchlanda): In LLVM/Clang, there are also `aarch64-elf-pauthabi-platform` // and `aarch64-elf-pauthabi-version` module flags. These are emitted into the // PAuth core info section of the resulting ELF, which the linker uses to enforce @@ -170,10 +170,13 @@ pub(crate) fn compile_codegen_unit( // // Link to PAuth core info documentation: // - if cx.sess().opts.unstable_opts.ptrauth_elf_got { + let cfg = cx.sess().pointer_auth_config.as_ref().unwrap(); + if cfg.elf_got { cx.add_ptrauth_elf_got_flag(); } - cx.add_ptrauth_sign_personality_flag(); + if cx.sess().pointer_authentication_functions() { + cx.add_ptrauth_sign_personality_flag(); + } } // Finalize code coverage by injecting the coverage map. Note, the coverage map will diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index e753fff61aa41..ec28eceeef691 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -26,7 +26,7 @@ use rustc_sanitizers::{cfi, kcfi}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::callconv::{FnAbi, PassMode}; -use rustc_target::spec::{Arch, HasTargetSpec, LlvmAbi, SanitizerSet, Target}; +use rustc_target::spec::{Arch, HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -2040,7 +2040,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llfn: &'ll Value, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, ) -> Option> { - if self.sess().target.llvm_abiname != LlvmAbi::Pauthtest { + if !self.sess().pointer_authentication_functions() { return None; } // Pointer authentication support is currently limited to extern "C" calls; filter out other diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 1c26738b7cf9c..030d62b25e92e 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -16,7 +16,7 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::{Instance, TyCtxt}; use rustc_session::cstore::DllImport; -use rustc_target::spec::LlvmAbi; +use rustc_session::{PointerAuthAddressDiscriminator, PointerAuthSchema}; use tracing::debug; use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; @@ -26,27 +26,13 @@ use crate::llvm::{ self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value, const_ptr_auth, }; -#[inline] -pub(crate) fn pauth_fn_attrs() -> &'static [&'static str] { - // FIXME(jchlanda) This is not an exhaustive list of all `ptrauth`-related attributes, but only - // those currently supported. The list is expected to grow as additional functionality is - // implemented, particularly for C++ interoperability. - &[ - "aarch64-jump-table-hardening", - "ptrauth-indirect-gotos", - "ptrauth-calls", - "ptrauth-returns", - "ptrauth-auth-traps", - ] -} - pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( cx: &CodegenCx<'ll, '_>, instance: Instance<'tcx>, llfn: &'ll llvm::Value, - pac: PacMetadata, + schema: &PointerAuthSchema, ) -> &'ll llvm::Value { - if cx.sess().target.llvm_abiname != LlvmAbi::Pauthtest { + if !cx.tcx.sess.pointer_authentication_functions() { return llfn; } @@ -68,16 +54,16 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( return llfn; } - let addr_diversity = match pac.addr_diversity { - AddressDiversity::None => None, - AddressDiversity::Real => Some(llfn), - AddressDiversity::Synthetic(val) => { + let addr_diversity = match schema.is_address_discriminated { + PointerAuthAddressDiscriminator::HardwareAddress(true) => Some(llfn), + PointerAuthAddressDiscriminator::HardwareAddress(false) => None, + PointerAuthAddressDiscriminator::Synthetic(val) => { let llval = cx.const_u64(val); let llty = cx.val_ty(llfn); Some(unsafe { llvm::LLVMConstIntToPtr(llval, llty) }) } }; - const_ptr_auth(llfn, pac.key, pac.disc, addr_diversity) + const_ptr_auth(llfn, schema.key as u32, schema.constant_discriminator as u64, addr_diversity) } /* @@ -331,7 +317,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { cv: Scalar, layout: abi::Scalar, llty: &'ll Type, - pac: Option, + schema: Option<&PointerAuthSchema>, ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { @@ -387,7 +373,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { value } } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, pac), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, schema), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a4d52c18c890b..0fb5aad428e7d 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; use rustc_span::Symbol; -use rustc_target::spec::{Arch, LlvmAbi}; +use rustc_target::spec::Arch; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; @@ -32,6 +32,7 @@ pub(crate) enum IsStatic { No, } /// Indicates whether a symbol is part of `.init_array` or `.fini_array`. +#[derive(PartialEq)] pub(crate) enum IsInitOrFini { Yes, No, @@ -120,23 +121,21 @@ pub(crate) fn const_alloc_to_llvm<'ll>( as u64; let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); - // Under pointer authentication, function pointers stored in init/fini arrays need special - // handling. - let pac_metadata = Some( - if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest - && matches!(is_init_fini, IsInitOrFini::Yes) - { - PacMetadata { - // Must correspond to ptrauth_key_init_fini_pointer from `ptrauth.h`. - key: 0, - // ptrauth_string_discriminator("init_fini") - disc: 0xd9d4, - addr_diversity: AddressDiversity::Synthetic(1), - } + let schema = if cx.sess().pointer_authentication() { + if is_init_fini == IsInitOrFini::Yes { + assert!(cx.sess().pointer_authentication_init_fini()); + cx.sess().pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()) + } else if cx.sess().pointer_authentication_functions() { + cx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) } else { - PacMetadata::default() - }, - ); + None + } + } else { + None + }; llvals.push(cx.scalar_to_backend_with_pac( InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx), Scalar::Initialized { @@ -144,7 +143,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( valid_range: WrappingRange::full(pointer_size), }, cx.type_ptr_ext(address_space), - pac_metadata, + schema, )); next_offset = offset + pointer_size_bytes; } @@ -221,7 +220,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( let fn_sig = sig.with(*header); let fn_abi = cx.fn_abi_of_fn_ptr(fn_sig, ty::List::empty()); // Decide if the initializer needs to be signed - if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest + if cx.sess().pointer_authentication() && matches!(fn_sig.abi(), ExternAbi::C { .. } | ExternAbi::System { .. }) { should_sign = true; diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 621f2cd3f9fc7..069a72c6b675c 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -20,10 +20,10 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; -use rustc_session::Session; use rustc_session::config::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet, }; +use rustc_session::{PointerAuthSchema, Session}; use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, sym}; use rustc_target::spec::{ Arch, CfgAbi, Env, FramePointer, HasTargetSpec, Os, RelocModel, SmallDataThresholdSupport, @@ -887,7 +887,11 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>, pac: Option) -> &'ll Value { + fn get_fn_addr( + &self, + instance: Instance<'tcx>, + schema: Option<&PointerAuthSchema>, + ) -> &'ll Value { // When pointer authentication metadata is provided, `get_fn_addr` will // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant // expression. @@ -901,8 +905,8 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { // , and comment in // builder's `ptrauth_operand_bundle`. let llfn = get_fn(self, instance); - match pac { - Some(pac) => common::maybe_sign_fn_ptr(self, instance, llfn, pac), + match schema { + Some(schema) => common::maybe_sign_fn_ptr(self, instance, llfn, schema), None => llfn, } } @@ -954,7 +958,10 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty::List::empty(), DUMMY_SP, ), - Some(PacMetadata::default()), + tcx.sess + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), ), _ => { let name = name.unwrap_or("rust_eh_personality"); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 54456ddea620b..015b22173830d 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -28,7 +28,7 @@ use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; -use rustc_target::spec::{Arch, LlvmAbi}; +use rustc_target::spec::Arch; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -37,7 +37,6 @@ use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::builder::gpu_offload::{ OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload, }; -use crate::common::pauth_fn_attrs; use crate::context::CodegenCx; use crate::declare::declare_raw_fn; use crate::errors::{ @@ -1712,9 +1711,12 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( hir::Safety::Unsafe, )); let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); - if cx.sess().target.llvm_abiname == LlvmAbi::Pauthtest { + + if cx.sess().pointer_authentication() { + let cfg = cx.sess().pointer_auth_config.as_ref().unwrap(); let attrs: Vec<&Attribute> = - pauth_fn_attrs().iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect(); + cfg.fn_attrs().into_iter().map(|name| llvm::CreateAttrString(cx.llcx, name)).collect(); + let (_ty, rust_try_fn) = rust_try; crate::attributes::apply_to_llfn(rust_try_fn, AttributePlace::Function, &attrs); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4e979df471318..642a5651bcbe6 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -493,7 +493,10 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return None; } - let main_llfn = cx.get_fn_addr(instance, Some(PacMetadata::default())); + let main_llfn = cx.get_fn_addr( + instance, + cx.sess().pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()), + ); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); @@ -554,7 +557,13 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.tcx().mk_args(&[main_ret_ty.into()]), DUMMY_SP, ); - let start_fn = cx.get_fn_addr(start_instance, Some(PacMetadata::default())); + let start_fn = cx.get_fn_addr( + start_instance, + cx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 3a2ff3ab5d2ae..cf3a9bb57313c 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -119,7 +119,10 @@ pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let instance = ty::Instance::mono(tcx, def_id); ( bx.fn_abi_of_instance(instance, ty::List::empty()), - bx.get_fn_addr(instance, Some(PacMetadata::default())), + bx.get_fn_addr( + instance, + tcx.sess.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()), + ), instance, ) } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index fad93de43b272..81ae98ba29d43 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -686,7 +686,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => ( false, - bx.get_fn_addr(drop_fn, Some(PacMetadata::default())), + bx.get_fn_addr( + drop_fn, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), bx.fn_abi_of_instance(drop_fn, ty::List::empty()), drop_fn, ), @@ -1102,7 +1108,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) .unwrap(); - (None, Some(bx.get_fn_addr(instance, Some(PacMetadata::default())))) + ( + None, + Some( + bx.get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), + ), + ) } _ => (Some(instance), None), } @@ -1415,7 +1432,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let fn_ptr = match (instance, llfn) { - (Some(instance), None) => bx.get_fn_addr(instance, Some(PacMetadata::default())), + (Some(instance), None) => bx.get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), (_, Some(llfn)) => llfn, _ => span_bug!(fn_span, "no instance or llfn for call"), }; diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f417dfba746f1..cdd1c9788bde5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -440,7 +440,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Immediate( bx.get_fn_addr( instance, - Some(PacMetadata::default()), + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), ), ) } @@ -459,7 +462,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Immediate( bx.cx().get_fn_addr( instance, - Some(PacMetadata::default()), + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), ), ) } @@ -681,7 +687,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }; - let fn_ptr = bx.get_fn_addr(instance, Some(PacMetadata::default())); + let fn_ptr = bx.get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ); let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 20738ba79e9fb..b4eba38d39c19 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -1,8 +1,8 @@ use rustc_abi as abi; use rustc_middle::mir::interpret::Scalar; +use rustc_session::PointerAuthSchema; use super::BackendTypes; -use crate::traits::PacMetadata; pub trait ConstCodegenMethods: BackendTypes { // Constant constructors @@ -47,7 +47,7 @@ pub trait ConstCodegenMethods: BackendTypes { cv: Scalar, layout: abi::Scalar, llty: Self::Type, - pac: Option, + schema: Option<&PointerAuthSchema>, ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index ecef986b55855..a8da0c0920d4a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -2,40 +2,11 @@ use std::cell::RefCell; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::{self, Instance, Ty}; -use rustc_session::Session; +use rustc_session::{PointerAuthSchema, Session}; use rustc_span::Symbol; use super::BackendTypes; -/// Strategy for incorporating address-based diversity into PAC computation. -#[derive(Default)] -pub enum AddressDiversity { - /// No address diversity is applied. - #[default] - None, - /// Use the actual memory address for diversification. - Real, - /// Use a fixed synthetic value instead of the real address, - /// i.e. `1` is used for `.init_array` / `.fini_array`. - Synthetic(u64), -} - -/// Metadata used for pointer authentication. -pub struct PacMetadata { - /// The PAC key to use. - pub key: u32, - /// Discriminator value used to diversify the PAC. - pub disc: u64, - /// Controls how address diversity is applied when computing the PAC. - pub addr_diversity: AddressDiversity, -} - -impl Default for PacMetadata { - fn default() -> Self { - PacMetadata { key: 0, disc: 0, addr_diversity: AddressDiversity::default() } - } -} - pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn vtables( &self, @@ -48,7 +19,11 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { ) { } fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function; - fn get_fn_addr(&self, instance: Instance<'tcx>, pac: Option) -> Self::Value; + fn get_fn_addr( + &self, + instance: Instance<'tcx>, + schema: Option<&PointerAuthSchema>, + ) -> Self::Value; fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; fn set_frame_pointer_type(&self, llfn: Self::Function); diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index ed2dad5a510cd..f46d07ea5008e 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -42,7 +42,7 @@ pub use self::coverageinfo::CoverageInfoBuilderMethods; pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; pub use self::declare::PreDefineCodegenMethods; pub use self::intrinsic::IntrinsicCallBuilderMethods; -pub use self::misc::{AddressDiversity, MiscCodegenMethods, PacMetadata}; +pub use self::misc::MiscCodegenMethods; pub use self::statics::{StaticBuilderMethods, StaticCodegenMethods}; pub use self::type_::{ ArgAbiBuilderMethods, BaseTypeCodegenMethods, DerivedTypeCodegenMethods, diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4492cdf2d2f8a..c652cef9fa17d 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1602,6 +1602,35 @@ pub struct BranchProtection { pub gcs: bool, } +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub enum PointerAuthOption { + Calls, + ReturnAddresses, + AuthTraps, + IndirectGotos, + ElfGot, + Aarch64JumpTableHardening, + FunctionPointerTypeDiscrimination, + InitFini, + InitFiniAddressDiscrimination, +} +impl PointerAuthOption { + pub fn parse(s: &str) -> Option { + match s { + "aarch64-jump-table-hardening" => Some(Self::Aarch64JumpTableHardening), + "auth-traps" => Some(Self::AuthTraps), + "calls" => Some(Self::Calls), + "elf-got" => Some(Self::ElfGot), + "function-pointer-type-discrimination" => Some(Self::FunctionPointerTypeDiscrimination), + "indirect-gotos" => Some(Self::IndirectGotos), + "init-fini" => Some(Self::InitFini), + "init-fini-address-discrimination" => Some(Self::InitFiniAddressDiscrimination), + "return-addresses" => Some(Self::ReturnAddresses), + _ => None, + } + } +} + pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { // First disallow some configuration given on the command line cfg::disallow_cfgs(sess, &user_cfg); @@ -3098,8 +3127,8 @@ pub(crate) mod dep_tracking { CoverageOptions, CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentMcount, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, - OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, Polonius, - ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, + OptLevel, OutFileName, OutputType, OutputTypes, PatchableFunctionEntry, PointerAuthOption, + Polonius, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, }; use crate::lint; @@ -3207,6 +3236,7 @@ pub(crate) mod dep_tracking { Align, CodegenRetagOptions, RustcVersion, + PointerAuthOption, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 2f44b4c102486..fc434fc651c5f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -771,6 +771,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; + pub(crate) const parse_pointer_authentication_list_with_polarity: &str = "a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses`"; pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`, `NoTT`"; pub(crate) const parse_offload: &str = "a comma separated list of settings: `Host=`, `Device`, `Test`"; @@ -1036,6 +1037,28 @@ pub mod parse { } } + pub(crate) fn parse_pointer_authentication_list_with_polarity( + slot: &mut Vec<(PointerAuthOption, bool)>, + v: Option<&str>, + ) -> bool { + match v { + Some(s) => { + for item in s.split(',') { + let Some(name) = item.strip_prefix(&['+', '-'][..]) else { + return false; + }; + let Some(opt) = PointerAuthOption::parse(name) else { + return false; // failed to parse, return. + }; + let enabled = &item[..1] == "+"; + slot.push((opt, enabled)); + } + true + } + None => false, + } + } + pub(crate) fn parse_fmt_debug(opt: &mut FmtDebug, v: Option<&str>) -> bool { *opt = match v { Some("full") => FmtDebug::Full, @@ -2628,6 +2651,18 @@ options! { "whether to use the PLT when calling into shared libraries; only has effect for PIC code on systems with ELF binaries (default: PLT is disabled if full relro is enabled on x86_64)"), + pointer_authentication: Vec<(PointerAuthOption, bool)> = (Vec::new(), parse_pointer_authentication_list_with_polarity, [TRACKED], + "A comma-separated list of pointer authentication options, each prefixed with `+` (enable) or `-` (disable). Available options: + `aarch64-jump-table-hardening` + `auth-traps` - enable hardened lowering for jump-table dispatch + `calls` - enable signing and authentication of all indirect calls + `elf-got` - enable authentication of pointers from GOT (ELF only) + `function-pointer-type-discrimination` - enable type discrimination on C function pointers + `indirect-gotos` - enable signing and authentication of indirect goto targets + `init-fini` - enable signing of function pointers in init/fini arrays + `init-fini-address-discrimination` - enable address discrimination in init/fini arrays + `return-addresses` - enable signing and authentication of return addresses + Example: `-Zpointer-authentication=+calls,-init-fini`."), polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], "enable polonius-based borrow-checker (default: no)"), pre_link_arg: (/* redirected to pre_link_args */) = ((), parse_string_push, [UNTRACKED], @@ -2661,7 +2696,6 @@ options! { "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), - ptrauth_elf_got: bool = (false, parse_bool, [TRACKED], "enable signing of ELF GOT entries"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 986dababf770a..8145589572490 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -39,7 +39,7 @@ pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, Varian use crate::config::{ self, Cfg, CheckCfg, CoverageLevel, CoverageOptions, CrateType, DebugInfo, ErrorOutputType, FunctionReturn, Input, InstrumentCoverage, InstrumentMcount, OptLevel, OutFileName, OutputType, - SwitchWithOptPath, + PointerAuthOption, SwitchWithOptPath, }; use crate::filesearch::FileSearch; use crate::lint::LintId; @@ -85,6 +85,191 @@ pub trait DynLintStore: Any + DynSync + DynSend { fn lint_groups_iter(&self) -> Box + '_>; } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PointerAuthKind { + None, + ARM8_3, +} + +/// Hardware pointer-signing keys in ARM8.3. +/// These values are the same as used in ptrauth.h. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PointerAuthARM8_3Key { + ASIA = 0, + ASIB = 1, + ASDA = 2, + ASDB = 3, +} + +/// Forms of extra discrimination. +pub enum PointerAuthDiscrimination { + /// No additional discrimination. + None, + /// Include a hash of the entity's type. + Type, + /// Include a hash of the entity's identity. + Decl, + /// Discriminate using a constant value. + Constant, +} + +/// Types of address discrimination. +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`. + Synthetic(u64), +} + +pub struct PointerAuthSchema { + pub kind: PointerAuthKind, + pub is_address_discriminated: PointerAuthAddressDiscriminator, + pub discrimination_kind: PointerAuthDiscrimination, + pub key: PointerAuthARM8_3Key, + pub constant_discriminator: u16, +} +impl PointerAuthSchema { + pub fn function_pointers_default(target: &Target) -> Self { + assert!(target.cfg_abi == CfgAbi::Pauthtest); + return Self { + kind: PointerAuthKind::ARM8_3, + is_address_discriminated: PointerAuthAddressDiscriminator::HardwareAddress(false), + discrimination_kind: PointerAuthDiscrimination::None, + key: PointerAuthARM8_3Key::ASIA, + constant_discriminator: 0, + }; + } + pub fn init_fini_default(target: &Target) -> Self { + assert!(target.cfg_abi == CfgAbi::Pauthtest); + return Self { + kind: PointerAuthKind::ARM8_3, + is_address_discriminated: PointerAuthAddressDiscriminator::Synthetic(1), + discrimination_kind: PointerAuthDiscrimination::None, + key: PointerAuthARM8_3Key::ASIA, + // ptrauth_string_discriminator("init_fini") + constant_discriminator: 0xd9d4, + }; + } +} + +pub struct PointerAuthConfig { + /// Should return addresses be authenticated? + pub return_addresses: bool, + /// Do authentication failures cause a trap? + pub auth_traps: bool, + /// Do indirect goto label addresses need to be authenticated? + pub indirect_gotos: bool, + /// Should ELF GOT entries be signed? + pub elf_got: bool, + /// Use hardened lowering for jump-table dispatch? + pub aarch64_jump_table_hardening: bool, + /// The ABI for C function pointers. + pub function_pointers: Option, + /// The ABI for function addresses in .init_array and .fini_array + pub init_fini: Option, +} +impl PointerAuthConfig { + fn default(target: &Target) -> Self { + assert!(target.cfg_abi == CfgAbi::Pauthtest); + return Self { + return_addresses: true, + auth_traps: true, + indirect_gotos: true, + elf_got: false, + aarch64_jump_table_hardening: true, + function_pointers: Some(PointerAuthSchema::function_pointers_default(target)), + init_fini: Some(PointerAuthSchema::init_fini_default(target)), + }; + } + pub fn from_raw(raw: &[(PointerAuthOption, bool)], target: &Target) -> Option { + if target.cfg_abi != CfgAbi::Pauthtest { + return None; + } + + let mut cfg = Self::default(target); + if raw.is_empty() { + return Some(cfg); + } + + for (opt, enabled) in raw { + match opt { + PointerAuthOption::Calls => { + if *enabled { + cfg.function_pointers.get_or_insert_with(|| { + PointerAuthSchema::function_pointers_default(target) + }); + } else { + cfg.function_pointers = None; + } + } + PointerAuthOption::FunctionPointerTypeDiscrimination => { + if *enabled { + let schema = cfg.function_pointers.get_or_insert_with(|| { + PointerAuthSchema::function_pointers_default(target) + }); + schema.discrimination_kind = PointerAuthDiscrimination::Type; + } else if let Some(schema) = &mut cfg.function_pointers { + schema.discrimination_kind = PointerAuthDiscrimination::None; + } + } + PointerAuthOption::ReturnAddresses => cfg.return_addresses = *enabled, + PointerAuthOption::AuthTraps => cfg.auth_traps = *enabled, + PointerAuthOption::IndirectGotos => cfg.indirect_gotos = *enabled, + PointerAuthOption::ElfGot => cfg.elf_got = *enabled, + PointerAuthOption::Aarch64JumpTableHardening => { + cfg.aarch64_jump_table_hardening = *enabled + } + PointerAuthOption::InitFini => { + if *enabled { + cfg.init_fini + .get_or_insert_with(|| PointerAuthSchema::init_fini_default(target)); + } else { + cfg.init_fini = None; + } + } + 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); + } + } + } + } + + Some(cfg) + } + pub fn fn_attrs(&self) -> Vec<&'static str> { + // FIXME(jchlanda) This is not an exhaustive list of all `ptrauth`-related attributes, but only + // those currently supported. The list is expected to grow as additional functionality is + // implemented, particularly for C++ interoperability. + let mut attrs = vec![]; + if self.aarch64_jump_table_hardening { + attrs.push("aarch64-jump-table-hardening"); + } + if self.auth_traps { + attrs.push("ptrauth-auth-traps"); + } + if self.function_pointers.is_some() { + attrs.push("ptrauth-calls"); + } + if self.indirect_gotos { + attrs.push("ptrauth-indirect-gotos"); + } + if self.return_addresses { + attrs.push("ptrauth-returns"); + } + + attrs + } +} + /// Represents the data associated with a compilation /// session for a single crate. pub struct Session { @@ -185,6 +370,9 @@ pub struct Session { /// Whether the test harness removed a user-written `#[rustc_main]` attribute /// while generating the synthetic test entry point. pub removed_rustc_main_attr: AtomicBool, + + /// Config specifying targets' pointer authentication preference. + pub pointer_auth_config: Option, } #[derive(Clone, Copy)] @@ -945,6 +1133,18 @@ impl Session { pub fn sanitizers(&self) -> SanitizerSet { return self.opts.unstable_opts.sanitizer | self.target.options.default_sanitizers; } + + pub fn pointer_authentication(&self) -> bool { + self.pointer_auth_config.is_some() + } + + pub fn pointer_authentication_functions(&self) -> bool { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()).is_some() + } + + pub fn pointer_authentication_init_fini(&self) -> bool { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()).is_some() + } } // JUSTIFICATION: part of session construction @@ -1104,6 +1304,9 @@ pub fn build_session( let timings = TimingSectionHandler::new(sopts.json_timings); + let pointer_auth_config: Option = + PointerAuthConfig::from_raw(&sopts.unstable_opts.pointer_authentication, &target); + let sess = Session { target, host, @@ -1138,6 +1341,7 @@ pub fn build_session( mir_opt_bisect_eval_count: AtomicUsize::new(0), used_features: Lock::default(), removed_rustc_main_attr: AtomicBool::new(false), + pointer_auth_config, }; validate_commandline_args_with_session_available(&sess); diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md index 558bc409e08d2..854cd5afaee42 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -407,6 +407,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 ui ui-fulldeps \ tests/assembly-llvm/pauth-basic.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 \ tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs \ diff --git a/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs new file mode 100644 index 0000000000000..90d664aaf9082 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs @@ -0,0 +1,64 @@ +// ignore-tidy-linelength +//@ only-pauthtest +//@ 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 + +//@[DEFAULT] needs-llvm-components: aarch64 +//@[DEFAULT] compile-flags: --target=aarch64-unknown-linux-pauthtest +//@[ALL] needs-llvm-components: aarch64 +//@[ALL] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=+aarch64-jump-table-hardening,+auth-traps,+calls,+indirect-gotos,+return-addresses +//@[DISABLE_JUMP] needs-llvm-components: aarch64 +//@[DISABLE_JUMP] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-aarch64-jump-table-hardening +//@[DISABLE_AUTH_TRAPS] needs-llvm-components: aarch64 +//@[DISABLE_AUTH_TRAPS] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-auth-traps +//@[DISABLE_CALLS] needs-llvm-components: aarch64 +//@[DISABLE_CALLS] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-calls +//@[DISABLE_INDIRCT_GOTOS] needs-llvm-components: aarch64 +//@[DISABLE_INDIRCT_GOTOS] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-indirect-gotos +//@[DISABLE_RETURNS] needs-llvm-components: aarch64 +//@[DISABLE_RETURNS] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-return-addresses +//@[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 + +// CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]] +fn main() {} +// DEFAULT: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DEFAULT-SAME: "ptrauth-auth-traps" +// DEFAULT-SAME: "ptrauth-calls" +// DEFAULT-SAME: "ptrauth-indirect-gotos" +// DEFAULT-SAME: "ptrauth-returns" + +// DISABLE_JUMP-NOT: aarch64-jump-table-hardening +// DISABLE_JUMP: attributes [[ATTR_MAIN]] = { {{.*}}"ptrauth-auth-traps" +// DISABLE_JUMP-SAME: "ptrauth-calls" +// DISABLE_JUMP-SAME: "ptrauth-indirect-gotos" +// DISABLE_JUMP-SAME: "ptrauth-returns" + +// DISABLE_AUTH_TRAPS-NOT: ptrauth-auth-traps +// DISABLE_AUTH_TRAPS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_AUTH_TRAPS-SAME: "ptrauth-calls" +// DISABLE_AUTH_TRAPS-SAME: "ptrauth-indirect-gotos" +// DISABLE_AUTH_TRAPS-SAME: "ptrauth-returns" + +// DISABLE_CALLS-NOT: ptrauth-calls +// DISABLE_CALLS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_CALLS-SAME: "ptrauth-auth-traps" +// DISABLE_CALLS-SAME: "ptrauth-indirect-gotos" +// DISABLE_CALLS-SAME: "ptrauth-returns" + +// DISABLE_INDIRCT_GOTOS-NOT: ptrauth-indirect-gotos +// DISABLE_INDIRCT_GOTOS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_INDIRCT_GOTOS-SAME: "ptrauth-auth-traps" +// DISABLE_INDIRCT_GOTOS-SAME: "ptrauth-calls" +// DISABLE_INDIRCT_GOTOS-SAME: "ptrauth-returns" + +// DISABLE_RETURNS-NOT: ptrauth-returns +// DISABLE_RETURNS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_RETURNS-SAME: "ptrauth-auth-traps" +// DISABLE_RETURNS-SAME: "ptrauth-calls" +// DISABLE_RETURNS-SAME: "ptrauth-indirect-gotos" + +// NONE-NOT: ptrauth-returns +// NONE-NOT: aarch64-jump-table-hardening +// NONE-NOT: ptrauth-auth-traps +// NONE-NOT: ptrauth-calls +// NONE-NOT: ptrauth-indirect-gotos diff --git a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs index 882cc4c6db971..2751494b9de7a 100644 --- a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -18,10 +18,10 @@ use std::panic; // CHECK-DAG: "ptrauth-returns" // CHECK: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" -// CHECK-DAG: "ptrauth-auth-traps" -// CHECK-DAG: "ptrauth-calls" -// CHECK-DAG: "ptrauth-indirect-gotos" -// CHECK-DAG: "ptrauth-returns" +// CHECK-SAME: "ptrauth-auth-traps" +// CHECK-SAME: "ptrauth-calls" +// CHECK-SAME: "ptrauth-indirect-gotos" +// CHECK-SAME: "ptrauth-returns" fn main() { let _ = panic::catch_unwind(|| { panic!("BOOM"); diff --git a/tests/codegen-llvm/pauth/pauth-extern-c.rs b/tests/codegen-llvm/pauth/pauth-extern-c.rs index ac12729f80699..5b0253a7bbe60 100644 --- a/tests/codegen-llvm/pauth/pauth-extern-c.rs +++ b/tests/codegen-llvm/pauth/pauth-extern-c.rs @@ -9,9 +9,9 @@ //@ [O3_PAUTH] needs-llvm-components: aarch64 //@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 //@ [O0_PAUTH-ELF-GOT] needs-llvm-components: aarch64 -//@ [O0_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Z ptrauth-elf-got +//@ [O0_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Z pointer-authentication=+elf-got //@ [O3_PAUTH-ELF-GOT] needs-llvm-components: aarch64 -//@ [O3_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Z ptrauth-elf-got +//@ [O3_PAUTH-ELF-GOT] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Z pointer-authentication=+elf-got //@ [O0_NO_PAUTH] needs-llvm-components: aarch64 //@ [O0_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=0 //@ [O3_NO_PAUTH] needs-llvm-components: aarch64 diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs index db327644d96cf..7d2399f71fdc2 100644 --- a/tests/codegen-llvm/pauth/pauth-init-fini.rs +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -1,14 +1,19 @@ //@ add-minicore // ignore-tidy-linelength //@ only-pauthtest -//@ revisions: O0_PAUTH O3_PAUTH +//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ADDR-DISC O3_PAUTH-ADDR-DISC //@ [O0_PAUTH] needs-llvm-components: aarch64 //@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O0_PAUTH-ADDR-DISC] needs-llvm-components: aarch64 +//@ [O0_PAUTH-ADDR-DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Zpointer-authentication=+init-fini-address-discrimination //@ [O3_PAUTH] needs-llvm-components: aarch64 //@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 +//@ [O3_PAUTH-ADDR-DISC] needs-llvm-components: aarch64 +//@ [O3_PAUTH-ADDR-DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Zpointer-authentication=+init-fini-address-discrimination -// Make sure that init/fini metadata uses correct discriminator: 0xd9d4/55764 +// Make sure that init/fini metadata uses correct discriminator: 0xd9d4/55764 - ptrauth_string_discriminator("init_fini"). +// And that address discriminator can be enabled. #![feature(no_core, lang_items)] #![no_std] @@ -20,12 +25,16 @@ use minicore::*; // 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" // 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" +// O0_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7init_fn), section ".init_array.90" +// O3_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7init_fn), section ".init_array.90" #[used] #[link_section = ".init_array.90"] static GLOBAL_INIT: extern "C" fn() = init_fn; // 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" // 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" +// O0_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7fini_fn), section ".fini_array.90" +// O3_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7fini_fn), section ".fini_array.90" #[used] #[link_section = ".fini_array.90"] static GLOBAL_FINI: extern "C" fn(i32) = fini_fn; diff --git a/tests/ui/README.md b/tests/ui/README.md index 85a5e6e0cbe1c..0a7dd5c2cd033 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1083,6 +1083,10 @@ See [Tracking issue for pin ergonomics #130494](https://github.com/rust-lang/rus See [`std::pin`](https://doc.rust-lang.org/std/pin/). +## `tests/ui/pointer_authentication/` + +Tests for `-Zpointer-authentication` compiler flag. + ## `tests/ui/precondition-checks/` Exercises on some unsafe precondition checks. diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr new file mode 100644 index 0000000000000..0cabccb610d32 --- /dev/null +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr @@ -0,0 +1,2 @@ +error: incorrect value `+I,+do,-not,-exist` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected + diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr new file mode 100644 index 0000000000000..0e11b66beeda7 --- /dev/null +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr @@ -0,0 +1,2 @@ +error: incorrect value `` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected + diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr new file mode 100644 index 0000000000000..72169649be288 --- /dev/null +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr @@ -0,0 +1,2 @@ +error: incorrect value `+elf-got,-imaginary` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected + diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs new file mode 100644 index 0000000000000..d3d1cbe82f059 --- /dev/null +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs @@ -0,0 +1,15 @@ +//@ revisions: empty unprefixed all_unknown all_known mixed + +//@[empty] compile-flags: -Zpointer-authentication= +//@[unprefixed] compile-flags: -Zpointer-authentication=auth-traps +//@[all_unknown] compile-flags: -Zpointer-authentication=+I,+do,-not,-exist +//@[all_known] check-pass +//@[all_known] compile-flags: -Zpointer-authentication=+elf-got,-init-fini +//@[mixed] compile-flags: -Zpointer-authentication=+elf-got,-imaginary + +fn main() {} + +//[empty]~? ERROR incorrect value `` for unstable option `pointer-authentication` +//[unprefixed]~? ERROR incorrect value `auth-traps` for unstable option `pointer-authentication` +//[all_unknown]~? ERROR incorrect value `+I,+do,-not,-exist` for unstable option `pointer-authentication` +//[mixed]~? ERROR incorrect value `+elf-got,-imaginary` for unstable option `pointer-authentication` diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr new file mode 100644 index 0000000000000..66b09af8df214 --- /dev/null +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr @@ -0,0 +1,2 @@ +error: incorrect value `auth-traps` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected + From 8559ce4f831bed68b1674e25c2b3a66d3f743377 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 20 May 2026 13:29:59 +0000 Subject: [PATCH 02/12] Extend version support with C++ specifics --- compiler/rustc_codegen_llvm/src/base.rs | 25 +++----- compiler/rustc_codegen_llvm/src/context.rs | 23 +++++++ compiler/rustc_session/src/config.rs | 16 +++-- compiler/rustc_session/src/options.rs | 10 ++- compiler/rustc_session/src/session.rs | 63 +++++++++++++++++++ .../pauth/pauth-attr-cli-flags.rs | 56 ++++++++++++++++- ...thentication_validation.all_unknown.stderr | 2 +- ...ter_authentication_validation.empty.stderr | 2 +- ...ter_authentication_validation.mixed.stderr | 2 +- ...uthentication_validation.unprefixed.stderr | 2 +- 10 files changed, 172 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 70f2ea4186d53..1f2df013d95c5 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -153,24 +153,15 @@ pub(crate) fn compile_codegen_unit( } if cx.sess().pointer_authentication() { - // FIXME(jchlanda): In LLVM/Clang, there are also `aarch64-elf-pauthabi-platform` - // and `aarch64-elf-pauthabi-version` module flags. These are emitted into the - // PAuth core info section of the resulting ELF, which the linker uses to enforce - // binary compatibility. - // - // We intentionally do not emit these flags now, since only a subset of features - // included in clang's pauthtest is currently supported. By default, the absence of - // this info is treated as compatible with any binary. - // - // Please note, that this would cause compatibility issues, specifically runtime - // crashes due to authentication failures (while compiling and linking - // successfully) when linking against binaries that support larger set of features - // (for example, signing of C++ member function pointers, virtual function - // pointers, virtual table pointers). - // - // Link to PAuth core info documentation: - // let cfg = cx.sess().pointer_auth_config.as_ref().unwrap(); + + let aarch64_elf_pauthabi_version = + cfg.calculate_pauth_abi_version(&cx.sess().target); + if aarch64_elf_pauthabi_version != 0 { + cx.add_ptrauth_pauthabi_version_and_platform_flags( + aarch64_elf_pauthabi_version, + ); + } if cfg.elf_got { cx.add_ptrauth_elf_got_flag(); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 069a72c6b675c..6c58c7d05b9e1 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -743,6 +743,29 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { ); } + pub(crate) fn add_ptrauth_pauthabi_version_and_platform_flags( + &self, + aarch64_elf_pauthabi_version: u32, + ) { + // NOTE: This must correspond to llvm's AARCH64_PAUTH_PLATFORM_LLVM_LINUX, as defined in + // /llvm/include/llvm/BinaryFormat/ELF.h. + // FIXME (jchlanda) extend possible values once we start supporting other platforms (for + // example: AARCH64_PAUTH_PLATFORM_BAREMETAL = 0x1); + let aarch64_pauth_platform_llvm_linux = 0x10000002; + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "aarch64-elf-pauthabi-platform", + aarch64_pauth_platform_llvm_linux, + ); + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "aarch64-elf-pauthabi-version", + aarch64_elf_pauthabi_version, + ); + } + // We do our best here to match what Clang does when compiling Objective-C natively. // See Clang's `CGObjCCommonMac::EmitImageInfo`: // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c652cef9fa17d..b1780a0a50111 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1604,15 +1604,19 @@ pub struct BranchProtection { #[derive(Clone, Copy, Hash, Debug, PartialEq)] pub enum PointerAuthOption { - Calls, - ReturnAddresses, + Aarch64JumpTableHardening, AuthTraps, - IndirectGotos, + Calls, ElfGot, - Aarch64JumpTableHardening, FunctionPointerTypeDiscrimination, + IndirectGotos, InitFini, InitFiniAddressDiscrimination, + Intrinsics, + ReturnAddresses, + TypeInfoVTPtrDisc, + VTPtrAddrDisc, + VTPtrTypeDisc, } impl PointerAuthOption { pub fn parse(s: &str) -> Option { @@ -1625,7 +1629,11 @@ impl PointerAuthOption { "indirect-gotos" => Some(Self::IndirectGotos), "init-fini" => Some(Self::InitFini), "init-fini-address-discrimination" => Some(Self::InitFiniAddressDiscrimination), + "intrinsics" => Some(Self::Intrinsics), "return-addresses" => Some(Self::ReturnAddresses), + "typeinfo-vt-ptr-discrimination" => Some(Self::TypeInfoVTPtrDisc), + "vt-ptr-addr-discrimination" => Some(Self::VTPtrAddrDisc), + "vt-ptr-type-discrimination" => Some(Self::VTPtrTypeDisc), _ => None, } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index fc434fc651c5f..5a29949989f7b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -771,7 +771,7 @@ mod desc { pub(crate) const parse_list: &str = "a space-separated list of strings"; pub(crate) const parse_list_with_polarity: &str = "a comma-separated list of strings, with elements beginning with + or -"; - pub(crate) const parse_pointer_authentication_list_with_polarity: &str = "a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses`"; + pub(crate) const parse_pointer_authentication_list_with_polarity: &str = "a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `intrinsics`, `return-addresses`, `typeinfo-vt-ptr-discrimination`, `vt-ptr-addr-discrimination` or `vt-ptr-type-discrimination`"; pub(crate) const parse_autodiff: &str = "a comma separated list of settings: `Enable`, `PrintSteps`, `PrintTA`, `PrintTAFn`, `PrintAA`, `PrintPerf`, `PrintModBefore`, `PrintModAfter`, `PrintModFinal`, `PrintPasses`, `NoPostopt`, `LooseTypes`, `Inline`, `NoTT`"; pub(crate) const parse_offload: &str = "a comma separated list of settings: `Host=`, `Device`, `Test`"; @@ -2653,15 +2653,19 @@ options! { (default: PLT is disabled if full relro is enabled on x86_64)"), pointer_authentication: Vec<(PointerAuthOption, bool)> = (Vec::new(), parse_pointer_authentication_list_with_polarity, [TRACKED], "A comma-separated list of pointer authentication options, each prefixed with `+` (enable) or `-` (disable). Available options: - `aarch64-jump-table-hardening` - `auth-traps` - enable hardened lowering for jump-table dispatch + `aarch64-jump-table-hardening` - enable hardened lowering for jump-table dispatch + `auth-traps` - trap immediately on pointer authentication failure `calls` - enable signing and authentication of all indirect calls `elf-got` - enable authentication of pointers from GOT (ELF only) `function-pointer-type-discrimination` - enable type discrimination on C function pointers `indirect-gotos` - enable signing and authentication of indirect goto targets `init-fini` - enable signing of function pointers in init/fini arrays `init-fini-address-discrimination` - enable address discrimination in init/fini arrays + `intrinsics` - pointer authentication intrinsics `return-addresses` - enable signing and authentication of return addresses + `typeinfo-vt-ptr-discrimination - incorporate type and address discrimination in authenticated vtable pointers for std::type_info + `vt-ptr-addr-discrimination - incorporate address discrimination in authenticated vtable pointers + `vt-ptr-type-discrimination - incorporate type discrimination in authenticated vtable pointers Example: `-Zpointer-authentication=+calls,-init-fini`."), polonius: Polonius = (Polonius::default(), parse_polonius, [TRACKED], "enable polonius-based borrow-checker (default: no)"), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 8145589572490..f56f97a20a38c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -168,6 +168,13 @@ pub struct PointerAuthConfig { pub function_pointers: Option, /// The ABI for function addresses in .init_array and .fini_array pub init_fini: Option, + /// Use of pointer authentication intrinsics. + pub intrinsics: bool, + /// The following are used only for compatibility with C++ and control over generated abi + /// version. They do not control Rust code generation. + pub typeinfo_vt_ptr_discrimination: bool, + pub vt_ptr_addr_discrimination: bool, + pub vt_ptr_type_discrimination: bool, } impl PointerAuthConfig { fn default(target: &Target) -> Self { @@ -180,8 +187,57 @@ impl PointerAuthConfig { aarch64_jump_table_hardening: true, function_pointers: Some(PointerAuthSchema::function_pointers_default(target)), init_fini: Some(PointerAuthSchema::init_fini_default(target)), + intrinsics: true, + typeinfo_vt_ptr_discrimination: true, + vt_ptr_addr_discrimination: true, + vt_ptr_type_discrimination: true, }; } + pub fn calculate_pauth_abi_version(&self, target: &Target) -> u32 { + assert!(target.cfg_abi == CfgAbi::Pauthtest); + // Bit positions of version flags for AARCH64_PAUTH_PLATFORM_LLVM_LINUX. + // NOTE: The enum values must stay in sync with clang, see: + // /llvm/include/llvm/BinaryFormat/ELF.h + // + // We do not expect to use C++ virtual dispatch, but enable these flags + // for compatibility with C++ code. Intrinsics are also always enabled. + // + // Link to PAuth core info documentation: + // + const INTRINSICS: u32 = 0; + const CALLS: u32 = 1; + const RETURNS: u32 = 2; + const AUTHTRAPS: u32 = 3; + const VT_PTR_ADDR_DISCR: u32 = 4; + const VT_PTR_TYPE_DISCR: u32 = 5; + const INIT_FINI: u32 = 6; + const INIT_FINI_ADDR_DISC: u32 = 7; + const GOT: u32 = 8; + const GOTOS: u32 = 9; + const TYPEINFO_VT_PTR_DISCR: u32 = 10; + // FIXME(jchlanda) We don't yet support function pointer type discrimination. + // const FPTR_TYPE_DISCR: u32 = 11; + + let pauth_abi_version: u32 = (u32::from(self.intrinsics) << INTRINSICS) + | (u32::from(self.function_pointers.is_some()) << CALLS) + | (u32::from(self.return_addresses) << RETURNS) + | (u32::from(self.auth_traps) << AUTHTRAPS) + | (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.elf_got) << GOT) + | (u32::from(self.indirect_gotos) << GOTOS) + | (u32::from(self.typeinfo_vt_ptr_discrimination) << TYPEINFO_VT_PTR_DISCR); + + pauth_abi_version + } pub fn from_raw(raw: &[(PointerAuthOption, bool)], target: &Target) -> Option { if target.cfg_abi != CfgAbi::Pauthtest { return None; @@ -240,6 +296,13 @@ impl PointerAuthConfig { PointerAuthAddressDiscriminator::Synthetic(1); } } + + PointerAuthOption::Intrinsics => cfg.intrinsics = *enabled, + PointerAuthOption::TypeInfoVTPtrDisc => { + cfg.typeinfo_vt_ptr_discrimination = *enabled + } + PointerAuthOption::VTPtrAddrDisc => cfg.vt_ptr_addr_discrimination = *enabled, + PointerAuthOption::VTPtrTypeDisc => cfg.vt_ptr_type_discrimination = *enabled, } } diff --git a/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs index 90d664aaf9082..3fe8df0c1bdcc 100644 --- a/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs +++ b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs @@ -16,8 +16,16 @@ //@[DISABLE_INDIRCT_GOTOS] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-indirect-gotos //@[DISABLE_RETURNS] needs-llvm-components: aarch64 //@[DISABLE_RETURNS] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-return-addresses +//@[DISABLE_INTRINSICS] needs-llvm-components: aarch64 +//@[DISABLE_INTRINSICS] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-intrinsics +//@[DISABLE_TYPEINFO] needs-llvm-components: aarch64 +//@[DISABLE_TYPEINFO] compile-flags: --target=aarch64-unknown-linux-pauthtest -Zpointer-authentication=-typeinfo-vt-ptr-discrimination +//@[DISABLE_VT_PTR_ADDR] needs-llvm-components: aarch64 +//@[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 //@[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 +//@[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 // CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]] fn main() {} @@ -26,39 +34,85 @@ fn main() {} // DEFAULT-SAME: "ptrauth-calls" // DEFAULT-SAME: "ptrauth-indirect-gotos" // DEFAULT-SAME: "ptrauth-returns" +// DEFAULT: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DEFAULT-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1791} // DISABLE_JUMP-NOT: aarch64-jump-table-hardening // DISABLE_JUMP: attributes [[ATTR_MAIN]] = { {{.*}}"ptrauth-auth-traps" // DISABLE_JUMP-SAME: "ptrauth-calls" // DISABLE_JUMP-SAME: "ptrauth-indirect-gotos" // DISABLE_JUMP-SAME: "ptrauth-returns" +// DISABLE_JUMP: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DISABLE_JUMP-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1791} // DISABLE_AUTH_TRAPS-NOT: ptrauth-auth-traps // DISABLE_AUTH_TRAPS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" // DISABLE_AUTH_TRAPS-SAME: "ptrauth-calls" // DISABLE_AUTH_TRAPS-SAME: "ptrauth-indirect-gotos" // DISABLE_AUTH_TRAPS-SAME: "ptrauth-returns" +// DISABLE_AUTH_TRAPS: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DISABLE_AUTH_TRAPS-NEXT !{i32 1, !"aarch64-elf-pauthabi-version", i32 1783} // DISABLE_CALLS-NOT: ptrauth-calls // DISABLE_CALLS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" // DISABLE_CALLS-SAME: "ptrauth-auth-traps" // DISABLE_CALLS-SAME: "ptrauth-indirect-gotos" // DISABLE_CALLS-SAME: "ptrauth-returns" +// DISABLE_CALLS: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DISABLE_CALLS-SAME-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1789} // DISABLE_INDIRCT_GOTOS-NOT: ptrauth-indirect-gotos // DISABLE_INDIRCT_GOTOS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" // DISABLE_INDIRCT_GOTOS-SAME: "ptrauth-auth-traps" // DISABLE_INDIRCT_GOTOS-SAME: "ptrauth-calls" // DISABLE_INDIRCT_GOTOS-SAME: "ptrauth-returns" +// DISABLE_INDIRCT_GOTOS: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1279} +// DISABLE_INDIRCT_GOTOS-NEXT: !{i32 1, !"ptrauth-sign-personality", i32 1} // DISABLE_RETURNS-NOT: ptrauth-returns // DISABLE_RETURNS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" // DISABLE_RETURNS-SAME: "ptrauth-auth-traps" // DISABLE_RETURNS-SAME: "ptrauth-calls" // DISABLE_RETURNS-SAME: "ptrauth-indirect-gotos" +// DISABLE_RETURNS: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DISABLE_RETURNS-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1787} + +// DISABLE_INTRINSICS: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_INTRINSICS-SAME: "ptrauth-auth-traps" +// DISABLE_INTRINSICS-SAME: "ptrauth-calls" +// DISABLE_INTRINSICS-SAME: "ptrauth-indirect-gotos" +// DISABLE_INTRINSICS-SAME: "ptrauth-returns" +// DISABLE_INTRINSICS: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DISABLE_INTRINSICS-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1790} + +// DISABLE_TYPEINFO: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_TYPEINFO-SAME: "ptrauth-auth-traps" +// DISABLE_TYPEINFO-SAME: "ptrauth-calls" +// DISABLE_TYPEINFO-SAME: "ptrauth-indirect-gotos" +// DISABLE_TYPEINFO-SAME: "ptrauth-returns" +// DISABLE_TYPEINFO: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DISABLE_TYPEINFO-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 767} + +// DISABLE_VT_PTR_ADDR: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_VT_PTR_ADDR-SAME: "ptrauth-auth-traps" +// DISABLE_VT_PTR_ADDR-SAME: "ptrauth-calls" +// DISABLE_VT_PTR_ADDR-SAME: "ptrauth-indirect-gotos" +// DISABLE_VT_PTR_ADDR-SAME: "ptrauth-returns" +// DISABLE_VT_PTR_ADDR: !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458} +// DISABLE_VT_PTR_ADDR-NEXT: !{i32 1, !"aarch64-elf-pauthabi-version", i32 1775} + +// DISABLE_VT_PTR_TYPE: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// DISABLE_VT_PTR_TYPE-SAME: "ptrauth-auth-traps" +// DISABLE_VT_PTR_TYPE-SAME: "ptrauth-calls" +// DISABLE_VT_PTR_TYPE-SAME: "ptrauth-indirect-gotos" +// DISABLE_VT_PTR_TYPE-SAME: "ptrauth-returns" +// 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} // NONE-NOT: ptrauth-returns // NONE-NOT: aarch64-jump-table-hardening // NONE-NOT: ptrauth-auth-traps // NONE-NOT: ptrauth-calls // NONE-NOT: ptrauth-indirect-gotos +// NONE-NOT: aarch64-elf-pauthabi-platform +// NONE-NOT: aarch64-elf-pauthabi-version diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr index 0cabccb610d32..47e61f2b8be73 100644 --- a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.all_unknown.stderr @@ -1,2 +1,2 @@ -error: incorrect value `+I,+do,-not,-exist` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected +error: incorrect value `+I,+do,-not,-exist` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `intrinsics`, `return-addresses`, `typeinfo-vt-ptr-discrimination`, `vt-ptr-addr-discrimination` or `vt-ptr-type-discrimination` was expected diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr index 0e11b66beeda7..9a4cd16c15a14 100644 --- a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.empty.stderr @@ -1,2 +1,2 @@ -error: incorrect value `` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected +error: incorrect value `` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `intrinsics`, `return-addresses`, `typeinfo-vt-ptr-discrimination`, `vt-ptr-addr-discrimination` or `vt-ptr-type-discrimination` was expected diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr index 72169649be288..ea8b9250f31b9 100644 --- a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.mixed.stderr @@ -1,2 +1,2 @@ -error: incorrect value `+elf-got,-imaginary` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected +error: incorrect value `+elf-got,-imaginary` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `intrinsics`, `return-addresses`, `typeinfo-vt-ptr-discrimination`, `vt-ptr-addr-discrimination` or `vt-ptr-type-discrimination` was expected diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr index 66b09af8df214..c6ff1e36350ee 100644 --- a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.unprefixed.stderr @@ -1,2 +1,2 @@ -error: incorrect value `auth-traps` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `return-addresses` was expected +error: incorrect value `auth-traps` for unstable option `pointer-authentication` - a comma-separated list of options, each of the form `+` or `-`, where `` is one of: `aarch64-jump-table-hardening`, `auth-traps`, `calls`, `elf-got`, `function-pointer-type-discrimination`, `indirect-gotos`, `init-fini`, `init-fini-address-discrimination`, `intrinsics`, `return-addresses`, `typeinfo-vt-ptr-discrimination`, `vt-ptr-addr-discrimination` or `vt-ptr-type-discrimination` was expected From 95508ff3066e179d13a7cc6fa957631d0d0fb76f Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 21 May 2026 13:11:35 +0000 Subject: [PATCH 03/12] Document -Zpointer-authentication option --- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/consts.rs | 31 ++++++---- compiler/rustc_codegen_llvm/src/context.rs | 8 +-- compiler/rustc_codegen_ssa/src/traits/misc.rs | 2 +- compiler/rustc_session/src/config.rs | 3 + compiler/rustc_session/src/errors.rs | 14 +++++ compiler/rustc_session/src/session.rs | 28 ++++++--- .../aarch64-unknown-linux-pauthtest.md | 57 ++++++++++++++++++- tests/codegen-llvm/pauth/pauth-init-fini.rs | 10 +++- ...nable_pointer_authentication_validation.rs | 21 +++++-- .../invalid_target_pointer_authentication.rs | 11 ++++ ...valid_target_pointer_authentication.stderr | 4 ++ ...on_not_supported_pointer_authentication.rs | 12 ++++ ...ot_supported_pointer_authentication.stderr | 4 ++ 14 files changed, 173 insertions(+), 34 deletions(-) create mode 100644 tests/ui/pointer_authentication/invalid_target_pointer_authentication.rs create mode 100644 tests/ui/pointer_authentication/invalid_target_pointer_authentication.stderr create mode 100644 tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs create mode 100644 tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index e20a1502a275d..0e3fa72fbcfb3 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -401,7 +401,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - _schema: Option<&PointerAuthSchema>, + _pointer_auth_schema: Option<&PointerAuthSchema>, ) -> RValue<'gcc> { let func_name = self.tcx.symbol_name(instance).name; diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 0fb5aad428e7d..aa2d976a9cab2 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -122,16 +122,27 @@ pub(crate) fn const_alloc_to_llvm<'ll>( let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); let schema = if cx.sess().pointer_authentication() { - if is_init_fini == IsInitOrFini::Yes { - assert!(cx.sess().pointer_authentication_init_fini()); - cx.sess().pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()) - } else if cx.sess().pointer_authentication_functions() { - cx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()) - } else { - None + match is_init_fini { + IsInitOrFini::Yes => { + if cx.sess().pointer_authentication_init_fini() { + cx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.init_fini.as_ref()) + } else { + None + } + } + IsInitOrFini::No => { + if cx.sess().pointer_authentication_functions() { + cx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + } else { + None + } + } } } else { None diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 6c58c7d05b9e1..aecf37b781587 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -751,12 +751,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { // /llvm/include/llvm/BinaryFormat/ELF.h. // FIXME (jchlanda) extend possible values once we start supporting other platforms (for // example: AARCH64_PAUTH_PLATFORM_BAREMETAL = 0x1); - let aarch64_pauth_platform_llvm_linux = 0x10000002; + const AARCH64_PAUTH_PLATFORM_LLVM_LINUX: u32 = 0x10000002; llvm::add_module_flag_u32( self.llmod, llvm::ModuleFlagMergeBehavior::Error, "aarch64-elf-pauthabi-platform", - aarch64_pauth_platform_llvm_linux, + AARCH64_PAUTH_PLATFORM_LLVM_LINUX, ); llvm::add_module_flag_u32( self.llmod, @@ -913,7 +913,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - schema: Option<&PointerAuthSchema>, + pointer_auth_schema: Option<&PointerAuthSchema>, ) -> &'ll Value { // When pointer authentication metadata is provided, `get_fn_addr` will // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant @@ -928,7 +928,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { // , and comment in // builder's `ptrauth_operand_bundle`. let llfn = get_fn(self, instance); - match schema { + match pointer_auth_schema { Some(schema) => common::maybe_sign_fn_ptr(self, instance, llfn, schema), None => llfn, } diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index a8da0c0920d4a..add7128a2974b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -22,7 +22,7 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn get_fn_addr( &self, instance: Instance<'tcx>, - schema: Option<&PointerAuthSchema>, + pointer_auth_schema: Option<&PointerAuthSchema>, ) -> Self::Value; fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index b1780a0a50111..e839a739f58a2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1604,6 +1604,9 @@ pub struct BranchProtection { #[derive(Clone, Copy, Hash, Debug, PartialEq)] pub enum PointerAuthOption { + // See and Clang's command line reference: + // + // for the origin and meaning of the enum values. Aarch64JumpTableHardening, AuthTraps, Calls, diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 31627887b662b..d1989609cefe2 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -381,6 +381,20 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } +#[derive(Diagnostic)] +#[diag("function pointer type discrimination is not supported")] +pub(crate) struct PointerAuthenticationTypeDiscriminationNotSupportedForTarget<'a> { + pub(crate) target_triple: &'a TargetTuple, +} + +#[derive(Diagnostic)] +#[diag( + "`-Z pointer-authentication` is not supported for target {$target_triple} and will be ignored" +)] +pub(crate) struct PointerAuthenticationNotSupportedForTarget<'a> { + pub(crate) target_triple: &'a TargetTuple, +} + #[derive(Diagnostic)] #[diag( "`-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored" diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f56f97a20a38c..97b9abf399edc 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -85,12 +85,6 @@ pub trait DynLintStore: Any + DynSync + DynSend { fn lint_groups_iter(&self) -> Box + '_>; } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum PointerAuthKind { - None, - ARM8_3, -} - /// Hardware pointer-signing keys in ARM8.3. /// These values are the same as used in ptrauth.h. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -123,7 +117,6 @@ pub enum PointerAuthAddressDiscriminator { } pub struct PointerAuthSchema { - pub kind: PointerAuthKind, pub is_address_discriminated: PointerAuthAddressDiscriminator, pub discrimination_kind: PointerAuthDiscrimination, pub key: PointerAuthARM8_3Key, @@ -133,7 +126,6 @@ impl PointerAuthSchema { pub fn function_pointers_default(target: &Target) -> Self { assert!(target.cfg_abi == CfgAbi::Pauthtest); return Self { - kind: PointerAuthKind::ARM8_3, is_address_discriminated: PointerAuthAddressDiscriminator::HardwareAddress(false), discrimination_kind: PointerAuthDiscrimination::None, key: PointerAuthARM8_3Key::ASIA, @@ -143,7 +135,6 @@ impl PointerAuthSchema { pub fn init_fini_default(target: &Target) -> Self { assert!(target.cfg_abi == CfgAbi::Pauthtest); return Self { - kind: PointerAuthKind::ARM8_3, is_address_discriminated: PointerAuthAddressDiscriminator::Synthetic(1), discrimination_kind: PointerAuthDiscrimination::None, key: PointerAuthARM8_3Key::ASIA, @@ -1437,6 +1428,25 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::LinkerPluginToWindowsNotSupported); } + if sess + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + .is_some_and(|schema| matches!(schema.discrimination_kind, PointerAuthDiscrimination::Type)) + { + sess.dcx().emit_err(errors::PointerAuthenticationTypeDiscriminationNotSupportedForTarget { + target_triple: &sess.opts.target_triple, + }); + } + + if sess.target.cfg_abi != CfgAbi::Pauthtest + && !sess.opts.unstable_opts.pointer_authentication.is_empty() + { + sess.dcx().emit_warn(errors::PointerAuthenticationNotSupportedForTarget { + target_triple: &sess.opts.target_triple, + }); + } + // Make sure that any given profiling data actually exists so LLVM can't // decide to silently skip PGO. if let Some(ref path) = sess.opts.cg.profile_use { diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md index 854cd5afaee42..89acecf4b65ec 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -356,6 +356,53 @@ linker = "/aarch64-unknown-linux-pauthtest-clang" Without it Cargo falls back to the system C toolchain (cc) and the compilation fails. +## Controlling pointer authentication features + +Pointer authentication behavior for this target can be configured using the +`-Zpointer-authentication` compiler option. The option accepts a comma-separated +list of values, each of the form `+` - to enable, or `-` - to +disable a feature, where `` is one of: +* `aarch64-jump-table-hardening` - enable hardened lowering for jump-table + dispatch +* `auth-traps` - trap immediately on pointer authentication failure +* `calls` - enable signing and authentication of indirect calls +* `elf-got` - enable authentication of pointers loaded from the ELF GOT +* `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-address-discrimination` - enable address discrimination for + init/fini array entries +* `intrinsics` - enable pointer authentication intrinsics +* `return-addresses` - enable signing and authentication of return addresses +* `typeinfo-vt-ptr-discrimination` - enable type/address discrimination for + authenticated `std::type_info` virtual table pointers +* `vt-ptr-addr-discrimination` - enable address discrimination for authenticated + virtual table pointers +* `vt-ptr-type-discrimination` - enable type discrimination for authenticated + virtual table pointers +For example: +`-Zpointer-authentication=+calls,+return-addresses,-init-fini`. + +Not all options are currently meaningful for Rust code itself. In particular, +the virtual table related ones: `typeinfo-vt-ptr-discrimination`, +`vt-ptr-addr-discrimination`, `vt-ptr-type-discrimination` exist primarily for +interoperability with C++ code and compatibility with the AArch64 Pointer +Authentication ELF ABI. Rust does not implement C++ virtual dispatch semantics, +authenticated C++ member function pointers, or authenticated virtual table +pointers. + +Similarly, `function-pointer-type-discrimination` is recognized for ABI +compatibility purposes, but full support is not yet implemented in Rust. + +Even when these features do not directly affect generated Rust code, they still +contribute to the emitted PAuth ABI metadata through the LLVM module flags: +`aarch64-elf-pauthabi-platform`, `aarch64-elf-pauthabi-version`. These flags are +emitted to communicate pointer authentication ABI requirements to the linker and +other toolchain components. The ABI version value is computed from the enabled +pointer authentication features according to the AArch64 ELF PAuth ABI +specification. The bit layout matches LLVM/Clang definitions. + ## Cross-compilation toolchains and C code This target supports interoperability with C code. A @@ -391,9 +438,12 @@ The following categories are supported (all present in tree): * End-to-end execution tests * Rust-driven quicksort (pauth-quicksort-rust-driver) * C-driven quicksort (pauth-quicksort-c-driver) -* UI error/warning reporting (the target does not support static linking) +* UI error/warning reporting * crt-static-pauthtest.rs * pauth-static-link-warning + * enable_pointer_authentication_validation.rs + * invalid_target_pointer_authentication.rs + * type_discrimination_not_supported_pointer_authentication.rs All tests from `assembly-llvm`, `codegen-llvm`, `codegen-units`, `coverage`, `crashes`, `incremental`, `library`, `mir-opt`, `run-make`, `ui` and @@ -416,7 +466,10 @@ x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \ tests/run-make/pauth-quicksort-rust-driver \ tests/run-make/pauth-quicksort-c-driver \ tests/run-make/pauth-static-link-warning \ - tests/ui/statics/crt-static-pauthtest.rs + tests/ui/statics/crt-static-pauthtest.rs \ + 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 ``` ## Limitations diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs index 7d2399f71fdc2..d457973b690f2 100644 --- a/tests/codegen-llvm/pauth/pauth-init-fini.rs +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -1,16 +1,20 @@ //@ add-minicore // ignore-tidy-linelength //@ only-pauthtest -//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ADDR-DISC O3_PAUTH-ADDR-DISC +//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ADDR-DISC O3_PAUTH-ADDR-DISC O0_PAUTH-NO-INIT-FINI O3_PAUTH-NO-INIT-FINI //@ [O0_PAUTH] needs-llvm-components: aarch64 //@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 //@ [O0_PAUTH-ADDR-DISC] needs-llvm-components: aarch64 //@ [O0_PAUTH-ADDR-DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Zpointer-authentication=+init-fini-address-discrimination +//@ [O0_PAUTH-NO-INIT-FINI] needs-llvm-components: aarch64 +//@ [O0_PAUTH-NO-INIT-FINI] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Zpointer-authentication=-init-fini //@ [O3_PAUTH] needs-llvm-components: aarch64 //@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 //@ [O3_PAUTH-ADDR-DISC] needs-llvm-components: aarch64 //@ [O3_PAUTH-ADDR-DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Zpointer-authentication=+init-fini-address-discrimination +//@ [O3_PAUTH-NO-INIT-FINI] needs-llvm-components: aarch64 +//@ [O3_PAUTH-NO-INIT-FINI] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Zpointer-authentication=-init-fini // Make sure that init/fini metadata uses correct discriminator: 0xd9d4/55764 - ptrauth_string_discriminator("init_fini"). // And that address discriminator can be enabled. @@ -27,6 +31,8 @@ use minicore::*; // 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" // O0_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7init_fn), section ".init_array.90" // O3_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7init_fn), section ".init_array.90" +// O0_PAUTH-NO-INIT-FINI-NOT: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth +// O0_PAUTH-NO-INIT-FINI-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth #[used] #[link_section = ".init_array.90"] static GLOBAL_INIT: extern "C" fn() = init_fn; @@ -35,6 +41,8 @@ static GLOBAL_INIT: extern "C" fn() = init_fn; // 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" // O0_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7fini_fn), section ".fini_array.90" // O3_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7fini_fn), section ".fini_array.90" +// O0_PAUTH-NO-INIT-FINI-NOT: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth +// O3_PAUTH-NO-INIT-FINI-NOT: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth #[used] #[link_section = ".fini_array.90"] static GLOBAL_FINI: extern "C" fn(i32) = fini_fn; diff --git a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs index d3d1cbe82f059..d7306508f39d4 100644 --- a/tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs +++ b/tests/ui/pointer_authentication/enable_pointer_authentication_validation.rs @@ -1,13 +1,22 @@ +//@ ignore-backends: gcc //@ revisions: empty unprefixed all_unknown all_known mixed -//@[empty] compile-flags: -Zpointer-authentication= -//@[unprefixed] compile-flags: -Zpointer-authentication=auth-traps -//@[all_unknown] compile-flags: -Zpointer-authentication=+I,+do,-not,-exist +//@[empty] needs-llvm-components: aarch64 +//@[empty] compile-flags: --target aarch64-unknown-linux-pauthtest -Zpointer-authentication= +//@[unprefixed] needs-llvm-components: aarch64 +//@[unprefixed] compile-flags: --target aarch64-unknown-linux-pauthtest -Zpointer-authentication=auth-traps +//@[all_unknown] needs-llvm-components: aarch64 +//@[all_unknown] compile-flags: --target aarch64-unknown-linux-pauthtest -Zpointer-authentication=+I,+do,-not,-exist //@[all_known] check-pass -//@[all_known] compile-flags: -Zpointer-authentication=+elf-got,-init-fini -//@[mixed] compile-flags: -Zpointer-authentication=+elf-got,-imaginary +//@[all_known] needs-llvm-components: aarch64 +//@[all_known] compile-flags: --target aarch64-unknown-linux-pauthtest -Zpointer-authentication=+elf-got,-init-fini +//@[mixed] needs-llvm-components: aarch64 +//@[mixed] compile-flags: --target aarch64-unknown-linux-pauthtest -Zpointer-authentication=+elf-got,-imaginary -fn main() {} +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] //[empty]~? ERROR incorrect value `` for unstable option `pointer-authentication` //[unprefixed]~? ERROR incorrect value `auth-traps` for unstable option `pointer-authentication` diff --git a/tests/ui/pointer_authentication/invalid_target_pointer_authentication.rs b/tests/ui/pointer_authentication/invalid_target_pointer_authentication.rs new file mode 100644 index 0000000000000..2d8b3b7a1915d --- /dev/null +++ b/tests/ui/pointer_authentication/invalid_target_pointer_authentication.rs @@ -0,0 +1,11 @@ +//@ ignore-backends: gcc +//@ check-pass +//@ needs-llvm-components: aarch64 + +//@ compile-flags: -Zpointer-authentication=-elf-got --crate-type=lib --target aarch64-unknown-linux-gnu + +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] +//~? WARN `-Z pointer-authentication` is not supported for target aarch64-unknown-linux-gnu and will be ignored diff --git a/tests/ui/pointer_authentication/invalid_target_pointer_authentication.stderr b/tests/ui/pointer_authentication/invalid_target_pointer_authentication.stderr new file mode 100644 index 0000000000000..1b1a33fd16c2b --- /dev/null +++ b/tests/ui/pointer_authentication/invalid_target_pointer_authentication.stderr @@ -0,0 +1,4 @@ +warning: `-Z pointer-authentication` is not supported for target aarch64-unknown-linux-gnu and will be ignored + +warning: 1 warning emitted + diff --git a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs new file mode 100644 index 0000000000000..6838e749fd333 --- /dev/null +++ b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs @@ -0,0 +1,12 @@ +//@ ignore-backends: gcc +//@ check-fail +//@ needs-llvm-components: aarch64 + +//@ compile-flags: -Zpointer-authentication=+function-pointer-type-discrimination --crate-type=lib --target aarch64-unknown-linux-pauthtest + +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] + +//~? ERROR function pointer type discrimination is not supported diff --git a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr new file mode 100644 index 0000000000000..c040b0cb61f66 --- /dev/null +++ b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr @@ -0,0 +1,4 @@ +error: function pointer type discrimination is not supported + +error: aborting due to 1 previous error + From 809c245fa9696feecb80d8bd520de20a5d05a949 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 3 Jul 2026 12:31:52 +0000 Subject: [PATCH 04/12] Convert `-Zpointer-authentication` to target modifier Also improve the API design of pointer_authentication_functions, by making it return Option<&PointerAuthSchema>, rather than bool. --- compiler/rustc_codegen_llvm/src/base.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 2 +- compiler/rustc_codegen_llvm/src/common.rs | 2 +- compiler/rustc_codegen_llvm/src/consts.rs | 22 +-------- compiler/rustc_codegen_llvm/src/context.rs | 5 +-- compiler/rustc_codegen_ssa/src/base.rs | 14 ++---- compiler/rustc_codegen_ssa/src/common.rs | 5 +-- compiler/rustc_codegen_ssa/src/mir/block.rs | 31 ++++--------- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 19 ++------ compiler/rustc_session/src/config.rs | 4 +- compiler/rustc_session/src/options.rs | 45 ++++++++++++------- compiler/rustc_session/src/session.rs | 8 ++-- .../aarch64-unknown-linux-pauthtest.md | 33 +++++++++++++- .../pauth/pauth-attr-cli-flags.rs | 13 +++++- tests/ui/target_modifiers/auxiliary/pauth.rs | 7 +++ .../incompatible_pauth.error_generated.stderr | 13 ++++++ .../ui/target_modifiers/incompatible_pauth.rs | 20 +++++++++ 17 files changed, 141 insertions(+), 104 deletions(-) create mode 100644 tests/ui/target_modifiers/auxiliary/pauth.rs create mode 100644 tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr create mode 100644 tests/ui/target_modifiers/incompatible_pauth.rs diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 1f2df013d95c5..79e5c8e6de6bb 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -165,7 +165,7 @@ pub(crate) fn compile_codegen_unit( if cfg.elf_got { cx.add_ptrauth_elf_got_flag(); } - if cx.sess().pointer_authentication_functions() { + if cx.sess().pointer_authentication_functions().is_some() { cx.add_ptrauth_sign_personality_flag(); } } diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index ec28eceeef691..408c258fa3475 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2040,7 +2040,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llfn: &'ll Value, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, ) -> Option> { - if !self.sess().pointer_authentication_functions() { + if self.sess().pointer_authentication_functions().is_none() { return None; } // Pointer authentication support is currently limited to extern "C" calls; filter out other diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 030d62b25e92e..205e2e9a14701 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -32,7 +32,7 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( llfn: &'ll llvm::Value, schema: &PointerAuthSchema, ) -> &'ll llvm::Value { - if !cx.tcx.sess.pointer_authentication_functions() { + if cx.tcx.sess.pointer_authentication_functions().is_none() { return llfn; } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index aa2d976a9cab2..20091d1b05be5 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -123,26 +123,8 @@ pub(crate) fn const_alloc_to_llvm<'ll>( let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); let schema = if cx.sess().pointer_authentication() { match is_init_fini { - IsInitOrFini::Yes => { - if cx.sess().pointer_authentication_init_fini() { - cx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.init_fini.as_ref()) - } else { - None - } - } - IsInitOrFini::No => { - if cx.sess().pointer_authentication_functions() { - cx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()) - } else { - None - } - } + IsInitOrFini::Yes => cx.sess().pointer_authentication_init_fini(), + IsInitOrFini::No => cx.sess().pointer_authentication_functions(), } } else { None diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index aecf37b781587..7ea30a5b4db6d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -981,10 +981,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty::List::empty(), DUMMY_SP, ), - tcx.sess - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), + tcx.sess.pointer_authentication_functions(), ), _ => { let name = name.unwrap_or("rust_eh_personality"); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 642a5651bcbe6..0711fbb72ee67 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -493,10 +493,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return None; } - let main_llfn = cx.get_fn_addr( - instance, - cx.sess().pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()), - ); + let main_llfn = cx.get_fn_addr(instance, cx.sess().pointer_authentication_functions()); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); @@ -557,13 +554,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.tcx().mk_args(&[main_ret_ty.into()]), DUMMY_SP, ); - let start_fn = cx.get_fn_addr( - start_instance, - cx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), - ); + let start_fn = + cx.get_fn_addr(start_instance, cx.sess().pointer_authentication_functions()); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index cf3a9bb57313c..ae72258a87c86 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -119,10 +119,7 @@ pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let instance = ty::Instance::mono(tcx, def_id); ( bx.fn_abi_of_instance(instance, ty::List::empty()), - bx.get_fn_addr( - instance, - tcx.sess.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()), - ), + bx.get_fn_addr(instance, tcx.sess.pointer_authentication_functions()), instance, ) } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 81ae98ba29d43..ab9972e4eea2c 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -686,13 +686,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => ( false, - bx.get_fn_addr( - drop_fn, - bx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), - ), + bx.get_fn_addr(drop_fn, bx.sess().pointer_authentication_functions()), bx.fn_abi_of_instance(drop_fn, ty::List::empty()), drop_fn, ), @@ -1110,15 +1104,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ( None, - Some( - bx.get_fn_addr( - instance, - bx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), - ), - ), + Some(bx.get_fn_addr( + instance, + bx.sess().pointer_authentication_functions(), + )), ) } _ => (Some(instance), None), @@ -1432,13 +1421,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let fn_ptr = match (instance, llfn) { - (Some(instance), None) => bx.get_fn_addr( - instance, - bx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), - ), + (Some(instance), None) => { + bx.get_fn_addr(instance, bx.sess().pointer_authentication_functions()) + } (_, Some(llfn)) => llfn, _ => span_bug!(fn_span, "no instance or llfn for call"), }; diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index cdd1c9788bde5..24b66f11a953e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -440,10 +440,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Immediate( bx.get_fn_addr( instance, - bx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), + bx.sess().pointer_authentication_functions(), ), ) } @@ -462,10 +459,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Immediate( bx.cx().get_fn_addr( instance, - bx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), + bx.sess().pointer_authentication_functions(), ), ) } @@ -687,13 +681,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }; - let fn_ptr = bx.get_fn_addr( - instance, - bx.sess() - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()), - ); + let fn_ptr = + bx.get_fn_addr(instance, bx.sess().pointer_authentication_functions()); let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e839a739f58a2..9563780af9d89 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1602,11 +1602,12 @@ pub struct BranchProtection { pub gcs: bool, } -#[derive(Clone, Copy, Hash, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] pub enum PointerAuthOption { // See and Clang's command line reference: // // for the origin and meaning of the enum values. + // tidy-alphabetical-start Aarch64JumpTableHardening, AuthTraps, Calls, @@ -1620,6 +1621,7 @@ pub enum PointerAuthOption { TypeInfoVTPtrDisc, VTPtrAddrDisc, VTPtrTypeDisc, + // tidy-alphabetical-end } impl PointerAuthOption { pub fn parse(s: &str) -> Option { diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5a29949989f7b..7b7311b0fe3ce 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1041,22 +1041,31 @@ pub mod parse { slot: &mut Vec<(PointerAuthOption, bool)>, v: Option<&str>, ) -> bool { - match v { - Some(s) => { - for item in s.split(',') { - let Some(name) = item.strip_prefix(&['+', '-'][..]) else { - return false; - }; - let Some(opt) = PointerAuthOption::parse(name) else { - return false; // failed to parse, return. - }; - let enabled = &item[..1] == "+"; - slot.push((opt, enabled)); - } - true - } - None => false, + let Some(s) = v else { + return false; + }; + + let mut map = BTreeMap::::new(); + + for item in s.split(',') { + let Some(name) = item.strip_prefix(&['+', '-'][..]) else { + return false; + }; + + let Some(opt) = PointerAuthOption::parse(name) else { + return false; + }; + + let enabled = item.starts_with('+'); + + // Last occurrence wins. + map.insert(opt, enabled); } + + slot.clear(); + slot.extend(map); + + true } pub(crate) fn parse_fmt_debug(opt: &mut FmtDebug, v: Option<&str>) -> bool { @@ -2651,7 +2660,11 @@ options! { "whether to use the PLT when calling into shared libraries; only has effect for PIC code on systems with ELF binaries (default: PLT is disabled if full relro is enabled on x86_64)"), - pointer_authentication: Vec<(PointerAuthOption, bool)> = (Vec::new(), parse_pointer_authentication_list_with_polarity, [TRACKED], + pointer_authentication: Vec<(PointerAuthOption, bool)> = ( + Vec::new(), + parse_pointer_authentication_list_with_polarity, + [TRACKED] + { TARGET_MODIFIER: PointerAuthentication }, "A comma-separated list of pointer authentication options, each prefixed with `+` (enable) or `-` (disable). Available options: `aarch64-jump-table-hardening` - enable hardened lowering for jump-table dispatch `auth-traps` - trap immediately on pointer authentication failure diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 97b9abf399edc..e35ff2e8cc76d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1192,12 +1192,12 @@ impl Session { self.pointer_auth_config.is_some() } - pub fn pointer_authentication_functions(&self) -> bool { - self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()).is_some() + pub fn pointer_authentication_functions(&self) -> Option<&PointerAuthSchema> { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()) } - pub fn pointer_authentication_init_fini(&self) -> bool { - self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()).is_some() + pub fn pointer_authentication_init_fini(&self) -> Option<&PointerAuthSchema> { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()) } } diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md index 89acecf4b65ec..e0f11c7800dcd 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -403,6 +403,35 @@ other toolchain components. The ABI version value is computed from the enabled pointer authentication features according to the AArch64 ELF PAuth ABI specification. The bit layout matches LLVM/Clang definitions. +### Option semantics and compatibility + +The `-Zpointer-authentication` option is a [target +modifier](https://rust-lang.github.io/rfcs/3716-target-modifiers.html). Target +modifiers are compiler options that affect the ABI, making it unsafe to link +together Rust crates built with different values. The selected pointer +authentication configuration becomes part of a crate's compilation +configuration, and `rustc` verifies that all crates in the dependency graph +agree on its value. If an incompatibility is detected, compilation is rejected +with an ABI mismatch error before invoking the linker, providing a clear +diagnostic. See `tests/ui/target_modifiers/incompatible_pauth.rs` for a sample +use case. + +The order of options is not significant. The compiler canonicalizes the +specified feature set before recording it, so the following are equivalent: + +```text +-Zpointer-authentication=+calls,+init-fini +-Zpointer-authentication=+init-fini,+calls +``` + +If the same option is specified multiple times, the last occurrence takes +precedence, matching Clang's behavior. For example, the following would leave +`init-fini` disable: + +```text +-Zpointer-authentication=+init-fini,-init-fini +``` + ## Cross-compilation toolchains and C code This target supports interoperability with C code. A @@ -444,6 +473,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 + * incompatible_pauth.rs All tests from `assembly-llvm`, `codegen-llvm`, `codegen-units`, `coverage`, `crashes`, `incremental`, `library`, `mir-opt`, `run-make`, `ui` and @@ -469,7 +499,8 @@ x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \ tests/ui/statics/crt-static-pauthtest.rs \ 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/type_discrimination_not_supported_pointer_authentication.rs \ + tests/ui/target_modifiers/incompatible_pauth.rs ``` ## Limitations diff --git a/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs index 3fe8df0c1bdcc..e8e87a9020f58 100644 --- a/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs +++ b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs @@ -2,6 +2,14 @@ //@ only-pauthtest //@ 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 +//@ add-minicore +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] + +extern crate minicore; + //@[DEFAULT] needs-llvm-components: aarch64 //@[DEFAULT] compile-flags: --target=aarch64-unknown-linux-pauthtest //@[ALL] needs-llvm-components: aarch64 @@ -27,8 +35,9 @@ //@[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 -// CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]] -fn main() {} +// CHECK: define {{.*}} @{{.*}}main{{.*}} [[ATTR_MAIN:#[0-9]+]] +#[inline(never)] +pub fn main() {} // DEFAULT: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" // DEFAULT-SAME: "ptrauth-auth-traps" // DEFAULT-SAME: "ptrauth-calls" diff --git a/tests/ui/target_modifiers/auxiliary/pauth.rs b/tests/ui/target_modifiers/auxiliary/pauth.rs new file mode 100644 index 0000000000000..761f4a520a4d2 --- /dev/null +++ b/tests/ui/target_modifiers/auxiliary/pauth.rs @@ -0,0 +1,7 @@ +//@ compile-flags: --target aarch64-unknown-linux-pauthtest -Zpointer-authentication=+calls,+init-fini +//@ needs-llvm-components: aarch64 +//@ only-pauthtest + +#![feature(no_core)] +#![crate_type = "rlib"] +#![no_core] diff --git a/tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr b/tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr new file mode 100644 index 0000000000000..ddc05e4b55322 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_pauth.error_generated.stderr @@ -0,0 +1,13 @@ +error: mixing `-Zpointer-authentication` will cause an ABI mismatch in crate `incompatible_pauth` + --> $DIR/incompatible_pauth.rs:14:1 + | +LL | #![feature(no_core)] + | ^ + | + = help: the `-Zpointer-authentication` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely + = note: `-Zpointer-authentication=+calls,-init-fini` in this crate is incompatible with `-Zpointer-authentication=+calls,+init-fini` in dependency `pauth` + = help: set `-Zpointer-authentication=+calls,+init-fini` in this crate or `-Zpointer-authentication=+calls,-init-fini` in `pauth` + = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=pointer-authentication` to silence this error + +error: aborting due to 1 previous error + diff --git a/tests/ui/target_modifiers/incompatible_pauth.rs b/tests/ui/target_modifiers/incompatible_pauth.rs new file mode 100644 index 0000000000000..6fe77a6916568 --- /dev/null +++ b/tests/ui/target_modifiers/incompatible_pauth.rs @@ -0,0 +1,20 @@ +//@ aux-build:pauth.rs +//@ revisions: ok ok_reverse_order ok_last_one_wins error_generated +//@ compile-flags: --target=aarch64-unknown-linux-pauthtest +//@ [ok] compile-flags: -Zpointer-authentication=+calls,+init-fini +//@ [ok] check-pass +//@ [ok_last_one_wins] compile-flags: -Zpointer-authentication=-calls,+calls,-init-fini,+init-fini +//@ [ok_last_one_wins] check-pass +//@ [ok_reverse_order] compile-flags: -Zpointer-authentication=+init-fini,+calls +//@ [ok_reverse_order] check-pass +//@ [error_generated] compile-flags: -Zpointer-authentication=+calls,-init-fini +//@ needs-llvm-components: aarch64 +//@ only-pauthtest + +#![feature(no_core)] +//[error_generated]~^ ERROR mixing `-Zpointer-authentication` will cause an ABI mismatch in crate +//`incompatible_pauth` +#![crate_type = "rlib"] +#![no_core] + +extern crate pauth; From 6b06a1f393d17bc56afcb86925d989a17d9e3fd0 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 11:13:30 +0000 Subject: [PATCH 05/12] [PAC] Introduce function pointer type encoder and hashing function This patch implements Rust's equivalent of Clang's function pointer type discriminator computation used in pointer authentication. Compatibility with Clang is a primary goal. The discriminator produced for a given external "C" function type must match the value computed by Clang so that function pointers can be exchanged safely between Rust and C code while preserving pointer authentication semantics. The implementation mirrors Clang's behavior in `ASTContext::encodeTypeForFunctionPointerAuth`, ensuring that identical C-compatible function types produce identical discriminators. See: . --- compiler/rustc_middle/src/lib.rs | 1 + .../rustc_middle/src/ptrauth/discriminator.rs | 426 ++++++++++++++++++ .../rustc_middle/src/ptrauth/llvm_siphash.rs | 142 ++++++ compiler/rustc_middle/src/ptrauth/mod.rs | 8 + 4 files changed, 577 insertions(+) create mode 100644 compiler/rustc_middle/src/ptrauth/discriminator.rs create mode 100644 compiler/rustc_middle/src/ptrauth/llvm_siphash.rs create mode 100644 compiler/rustc_middle/src/ptrauth/mod.rs diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index da7ad32ae9dc0..d6e4ade488390 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -81,6 +81,7 @@ pub mod metadata; pub mod middle; pub mod mir; pub mod mono; +pub mod ptrauth; pub mod queries; pub mod query; pub mod thir; diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs new file mode 100644 index 0000000000000..ed5147dc9ca40 --- /dev/null +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -0,0 +1,426 @@ +/*! +Function pointer type discrimination for pointer authentication. + +This module implements Rust's equivalent of Clang's function pointer type +discriminator computation used in pointer authentication. + +Compatibility with Clang is a primary goal. The discriminator produced for a +given external "C" function type must match the value computed by Clang so that +function pointers can be exchanged safely between Rust and C code while +preserving pointer authentication semantics. + +The implementation mirrors Clang's behavior in +`ASTContext::encodeTypeForFunctionPointerAuth`, ensuring that identical +C-compatible function types produce identical discriminators. See: +. + +## Overview + +The computation is structured into three conceptual stages: + +### 1. Type normalization and lowering + Rust types are converted into a language-independent representation + (`ClangDiscTy`) that mirrors the type categories used by Clang when computing + function pointer discriminators. This includes canonicalization such as + treating all pointer-like types uniformly and mapping Rust constructs onto + their closest C equivalents. + +### 2. Type encoding + The lowered representation is serialized into a byte stream using rules + intended to match Clang's implementation in: + `encodeTypeForFunctionPointerAuth`. The resulting encoding describes the + function signature in a target-independent form suitable for hashing. + +### 3. Discriminator hashing + The encoded byte stream is hashed using LLVM's stable SipHash-2-4 based + discriminator algorithm. The implementation here is a direct translation + of LLVM/Clang's logic and must remain bit-for-bit compatible. See: + . + Defined in `llvm_siphash.rs`. + +## Module structure + +- Public API + - `FnPtrTypeDiscriminatorInput` + - `build_fn_ptr_type_discriminator_input_from_instance` + - `build_fn_ptr_type_discriminator_input_from_sig` + - `build_fn_ptr_type_discriminator_input_from_ty` + - `compute_fn_ptr_type_discriminator` + +- Signature extraction + - `extract_fn_ptr_type` + +- Clang-compatible type model + - `ClangDiscTy` + - `canonicalize_c_type` + - `to_clang_disc_ty` + +- Encoding + - `PtrauthEncoder` + - `encode_ty` + +## Compatibility requirements + +Any changes to the encoding or hashing logic should be validated against Clang's +discriminator computation. Divergence from Clang will result in incompatible +pointer authentication values across language boundaries. + +This implementation intentionally approximates Clang's behavior for extern "C" +function types only. It does NOT attempt to model full type system rules. +*/ + +use rustc_abi::ExternAbi; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, Unnormalized}; +use rustc_span::sym; + +use crate::ptrauth::llvm_siphash::llvm_pointer_auth_stable_siphash; + +/// Canonical representation of a function signature used for pointer +/// authentication discriminator generation. +#[derive(Debug)] +pub struct FnPtrTypeDiscriminatorInput<'tcx> { + inputs: &'tcx [Ty<'tcx>], + output: Ty<'tcx>, + abi: ExternAbi, + c_variadic: bool, +} + +impl<'tcx> FnPtrTypeDiscriminatorInput<'tcx> { + fn from_sig(sig: ty::FnSig<'tcx>) -> Self { + FnPtrTypeDiscriminatorInput { + inputs: sig.inputs(), + output: sig.output(), + abi: sig.abi(), + c_variadic: sig.c_variadic(), + } + } + + fn from_sig_tys(sig: ty::FnSigTys>, header: &ty::FnHeader>) -> Self { + FnPtrTypeDiscriminatorInput { + inputs: sig.inputs(), + output: sig.output(), + abi: header.abi(), + c_variadic: header.c_variadic(), + } + } +} + +/// Unwraps optional function pointers and normalizes the type. +/// +/// Only `Option` is supported for nullability modeling, matching C ABI +/// null pointer conventions. +pub fn extract_fn_ptr_type<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Option> { + ty = tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), Unnormalized::new(ty)); + + loop { + match ty.kind() { + ty::Adt(def, args) if tcx.is_diagnostic_item(sym::Option, def.did()) => { + ty = args.type_at(0); + continue; + } + + ty::FnPtr(..) | ty::FnDef(..) => { + return Some(ty); + } + + _ => return None, + } + } +} + +/// Builds type discrimination input from a Rust function type. +/// +/// Accepts both: +/// - `FnPtr`: actual function pointer types +/// - `FnDef`: function items +/// +/// FnDef is only accepted for convenience; the discriminator is still computed +/// from the instantiated function signature. +pub fn build_fn_ptr_type_discriminator_input_from_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option> { + let ty = extract_fn_ptr_type(tcx, ty)?; + + match ty.kind() { + ty::FnPtr(sig, header) => { + let sig = sig.skip_binder(); + + Some(FnPtrTypeDiscriminatorInput::from_sig_tys(sig, header)) + } + + ty::FnDef(def_id, args) => { + let sig = tcx.fn_sig(*def_id).instantiate(tcx, args).skip_binder(); + + Some(FnPtrTypeDiscriminatorInput::from_sig(sig)) + } + + _ => None, + } +} + +/// Builds type discrimination input from a monomorphized function instance. +/// +/// The instance's signature is instantiated using its generic arguments and +/// normalized before constructing the canonical discriminator input. Unlike +/// `build_fn_ptr_type_discriminator_input_from_ty`, this function cannot fail +/// because an `Instance` always represents a callable item with a well-defined +/// function signature. +pub fn build_fn_ptr_type_discriminator_input_from_instance<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> FnPtrTypeDiscriminatorInput<'tcx> { + let sig = tcx + .instantiate_and_normalize_erasing_regions( + instance.args, + ty::TypingEnv::fully_monomorphized(), + tcx.fn_sig(instance.def_id()), + ) + .skip_binder(); + + FnPtrTypeDiscriminatorInput::from_sig(sig) +} + +/// Builds type discrimination input from a function signature. +/// +/// The signature is assumed to already be instantiated and normalized. +pub fn build_fn_ptr_type_discriminator_input_from_sig<'tcx>( + sig: ty::FnSig<'tcx>, +) -> FnPtrTypeDiscriminatorInput<'tcx> { + FnPtrTypeDiscriminatorInput::from_sig(sig) +} + +pub fn compute_fn_ptr_type_discriminator<'tcx>( + tcx: TyCtxt<'tcx>, + input: &FnPtrTypeDiscriminatorInput<'tcx>, +) -> u64 { + if !matches!(input.abi, ExternAbi::C { .. } | ExternAbi::System { .. }) { + return 0; + } + + let mut enc = PtrauthEncoder::new(); + enc.push(b'F'); + + encode_ty(&mut enc, tcx, input.output); + + for &arg in input.inputs { + encode_ty(&mut enc, tcx, arg); + } + + if input.c_variadic { + enc.push(b'z'); + } + + enc.push(b'E'); + + let hash = enc.finish(); + + hash.into() +} + +// Clang disc type. +#[derive(Debug)] +enum ClangDiscTy<'tcx> { + Int, + Float(&'tcx ty::FloatTy), + Bool, + Char, + + // Pointer-like types in the C ABI sense. + // This includes: + // - raw pointers (`*const T`, `*mut T`) + // - Rust references (`&T`, `&mut T`) + // - function pointers + // All collapse to a single Clang-compatible 'P' node. + Pointer, + + Array { elem: Ty<'tcx> }, + + // FIXME(jchlands) Decide if to support Complex types. Clang has dedicated node for this + // `Type::Complex`, Rust does not. So we match against a Tuple(FP_TYPE, FP_TYPE), that should + // not be a problem for extern "C". + Complex(Ty<'tcx>), + + Vector { bytes: u64 }, + + EnumLikeInt, + AdtName(String), + Opaque, + Void, +} + +// Lowering (Rust Ty -> ClangDiscTy) +fn is_representing_c_complex(fields: &[Ty<'_>]) -> bool { + fields.len() == 2 && fields[0] == fields[1] && is_complex_compatible_float(fields[0]) +} + +fn is_complex_compatible_float(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Float(f) => match f.bit_width() { + 32 => true, + 64 => true, + 128 => true, + _ => false, + }, + _ => false, + } +} + +// Canonicalize `Option` to `fn ptr`. This is so that we can express C's null ptr argument. +// Please see pauth-fn-ptr-type-discrimination-null-arg.rs test for an example. +fn canonicalize_c_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Adt(def, args) = ty.kind() + && tcx.is_diagnostic_item(sym::Option, def.did()) + { + let inner = args.type_at(0); + + if matches!(inner.kind(), ty::FnPtr(..)) { + return inner; + } + } + + ty +} + +/// Lowers a Rust type into a Clang-compatible discriminator type. +/// +/// This is not a full semantic translation of Rust types. It is a lossy mapping +/// that intentionally matches Clang's function pointer authentication encoding +/// rules. +/// +/// Important invariants: +/// - All pointer-like types (Rust refs, raw pointers, fn pointers) collapse to +/// `Pointer`. +/// - Struct/union types are encoded using name only, not layout. +/// - Enums are treated as integers. +/// - SIMD types are encoded only by total byte size (no lane semantics). +/// This must remain in sync with Clang's `encodeTypeForFunctionPointerAuth`. +fn to_clang_disc_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ClangDiscTy<'tcx> { + let ty = canonicalize_c_type(tcx, ty); + match ty.kind() { + // C void / Rust () + ty::Tuple(list) if list.is_empty() => ClangDiscTy::Void, + + // Complex + ty::Tuple(fields) if is_representing_c_complex(fields) => ClangDiscTy::Complex(fields[0]), + + // scalars + ty::Bool => ClangDiscTy::Bool, + ty::Char => ClangDiscTy::Char, + + ty::Int(_) | ty::Uint(_) => ClangDiscTy::Int, + ty::Float(f) => ClangDiscTy::Float(f), + + // everything pointer-like collapses + ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) | ty::Dynamic(..) | ty::Slice(_) | ty::Str => { + ClangDiscTy::Pointer + } + + // arrays ignore size + ty::Array(elem, _) => ClangDiscTy::Array { elem: *elem }, + + // enums to integer collapse + ty::Adt(def, _) if def.is_enum() => ClangDiscTy::EnumLikeInt, + // simd vectors + ty::Adt(def, args) if def.repr().simd() => { + // Clang encodes SIMD vectors by their total size + let input = ty::PseudoCanonicalInput { + typing_env: ty::TypingEnv::fully_monomorphized(), + value: ty, + }; + + let Ok(layout) = tcx.layout_of(input) else { + tcx.dcx().delayed_bug("could not compute SIMD layout"); + return ClangDiscTy::Opaque; + }; + + let bytes = layout.size.bytes(); + + ClangDiscTy::Vector { bytes } + } + // structs/unions to name-based identity + ty::Adt(def, _) => { + let name = tcx.item_name(def.did()).to_string(); + ClangDiscTy::AdtName(name) + } + + ty::Foreign(_) => ClangDiscTy::Opaque, + + _ => ClangDiscTy::Opaque, + } +} + +// Encoder +struct PtrauthEncoder { + buf: Vec, +} + +impl PtrauthEncoder { + fn new() -> Self { + Self { buf: Vec::new() } + } + + fn push(&mut self, b: u8) { + self.buf.push(b); + } + + fn push_str(&mut self, s: &str) { + self.buf.extend_from_slice(s.as_bytes()); + } + + fn finish(&self) -> u16 { + llvm_pointer_auth_stable_siphash(&self.buf) + } +} + +/// Encodes a ClangDiscTy into the discriminator byte stream. +/// +/// This format is intended to be bit-for-bit compatible with Clang's +/// `encodeTypeForFunctionPointerAuth`. +fn encode_ty<'tcx>(enc: &mut PtrauthEncoder, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { + let cty = to_clang_disc_ty(tcx, ty); + + match cty { + // scalars + ClangDiscTy::Bool | ClangDiscTy::Char | ClangDiscTy::Int => enc.push(b'i'), + + ClangDiscTy::Float(f) => match f.bit_width() { + 16 => enc.push_str("Dh"), + 32 => enc.push(b'f'), + 64 => enc.push(b'd'), + 128 => enc.push(b'g'), + _ => enc.push(b'?'), + }, + + ClangDiscTy::Void => enc.push(b'v'), + + // pointer boundary (NO RECURSION) + ClangDiscTy::Pointer => enc.push(b'P'), + + // arrays ignore size + ClangDiscTy::Array { elem } => { + enc.push(b'A'); + encode_ty(enc, tcx, elem); + } + + // enums collapse + ClangDiscTy::EnumLikeInt => enc.push(b'i'), + + // ADT identity + ClangDiscTy::AdtName(name) => { + enc.push_str(&name.len().to_string()); + enc.push_str(&name); + } + + ClangDiscTy::Opaque => enc.push(b'?'), + + ClangDiscTy::Complex(t) => { + enc.push(b'C'); + encode_ty(enc, tcx, t); + } + ClangDiscTy::Vector { bytes } => { + enc.push_str("Dv"); + enc.push_str(&bytes.to_string()); + } + } +} diff --git a/compiler/rustc_middle/src/ptrauth/llvm_siphash.rs b/compiler/rustc_middle/src/ptrauth/llvm_siphash.rs new file mode 100644 index 0000000000000..68aeac11a0429 --- /dev/null +++ b/compiler/rustc_middle/src/ptrauth/llvm_siphash.rs @@ -0,0 +1,142 @@ +// LLVM SipHash-2-4 +pub fn llvm_pointer_auth_stable_siphash(data: &[u8]) -> u16 { + let raw = llvm_siphash_2_4_64(data); + + ((raw % 0xFFFF) + 1) as u16 +} + +const SIPHASH_KEY: [u8; 16] = [ + 0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79, 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4, +]; + +#[inline(always)] +fn rotl(x: u64, b: u32) -> u64 { + (x << b) | (x >> (64 - b)) +} + +#[inline(always)] +fn sipround(v0: &mut u64, v1: &mut u64, v2: &mut u64, v3: &mut u64) { + *v0 = v0.wrapping_add(*v1); + *v1 = rotl(*v1, 13); + *v1 ^= *v0; + *v0 = rotl(*v0, 32); + + *v2 = v2.wrapping_add(*v3); + *v3 = rotl(*v3, 16); + *v3 ^= *v2; + + *v0 = v0.wrapping_add(*v3); + *v3 = rotl(*v3, 21); + *v3 ^= *v0; + + *v2 = v2.wrapping_add(*v1); + *v1 = rotl(*v1, 17); + *v1 ^= *v2; + *v2 = rotl(*v2, 32); +} + +fn u64_from_le(bytes: &[u8]) -> u64 { + u64::from_le_bytes(bytes.try_into().unwrap()) +} + +fn load_u64_partial(bytes: &[u8]) -> u64 { + let mut b = 0u64; + + match bytes.len() { + 7 => { + b |= (bytes[6] as u64) << 48; + b |= (bytes[5] as u64) << 40; + b |= (bytes[4] as u64) << 32; + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 6 => { + b |= (bytes[5] as u64) << 40; + b |= (bytes[4] as u64) << 32; + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 5 => { + b |= (bytes[4] as u64) << 32; + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 4 => { + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 3 => { + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 2 => { + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 1 => { + b |= bytes[0] as u64; + } + _ => {} + } + + b +} + +// LLVM siphash<2,4> 64-bit output +fn llvm_siphash_2_4_64(data: &[u8]) -> u64 { + let k0 = u64_from_le(&SIPHASH_KEY[0..8]); + let k1 = u64_from_le(&SIPHASH_KEY[8..16]); + + let mut v0: u64 = 0x736f6d6570736575 ^ k0; + let mut v1: u64 = 0x646f72616e646f6d ^ k1; + let mut v2: u64 = 0x6c7967656e657261 ^ k0; + let mut v3: u64 = 0x7465646279746573 ^ k1; + + let mut b: u64 = (data.len() as u64) << 56; + let mut i = 0; + + // compression + while i + 8 <= data.len() { + let m = u64_from_le(&data[i..i + 8]); + i += 8; + + v3 ^= m; + + for _ in 0..2 { + sipround(&mut v0, &mut v1, &mut v2, &mut v3); + } + + v0 ^= m; + } + + // tail + let tail = &data[i..]; + + b |= load_u64_partial(tail); + + v3 ^= b; + + for _ in 0..2 { + sipround(&mut v0, &mut v1, &mut v2, &mut v3); + } + + v0 ^= b; + + // finalization + v2 ^= 0xff; + + for _ in 0..4 { + sipround(&mut v0, &mut v1, &mut v2, &mut v3); + } + + v0 ^ v1 ^ v2 ^ v3 +} diff --git a/compiler/rustc_middle/src/ptrauth/mod.rs b/compiler/rustc_middle/src/ptrauth/mod.rs new file mode 100644 index 0000000000000..039fa1bb1a1ff --- /dev/null +++ b/compiler/rustc_middle/src/ptrauth/mod.rs @@ -0,0 +1,8 @@ +pub mod discriminator; +pub mod llvm_siphash; + +pub use discriminator::{ + FnPtrTypeDiscriminatorInput, build_fn_ptr_type_discriminator_input_from_instance, + build_fn_ptr_type_discriminator_input_from_sig, build_fn_ptr_type_discriminator_input_from_ty, + compute_fn_ptr_type_discriminator, +}; From bcf32dfaae155850ee179b8e584b6ca17a8fa244 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:01:47 +0000 Subject: [PATCH 06/12] [PAC] Include discriminator in `FnAbi`, add `llvm.ptrauth.resign` This patch introduces the following: * Extends `FnAbi` (`callconv`) with a `ptrauth_type_discriminator` field. This field is only used when emitting pointer authentication call bundles. It is stored in `FnAbi` because the call site is not guaranteed to have access to an `Instance`, so the discriminator cannot always be computed on demand. * Adds support for `llvm.ptrauth.resign`. This intrinsic will be used when support for semantic transmute is added. * Performs a minor API redesign as groundwork for allowing call sites to modify schemas in place. --- compiler/rustc_codegen_gcc/src/builder.rs | 11 ++++++ compiler/rustc_codegen_gcc/src/common.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_gcc/src/int.rs | 1 + compiler/rustc_codegen_llvm/src/builder.rs | 34 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/common.rs | 8 ++--- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../rustc_codegen_ssa/src/traits/builder.rs | 9 +++++ .../rustc_codegen_ssa/src/traits/consts.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/misc.rs | 2 +- compiler/rustc_session/src/session.rs | 25 +++++++++++--- compiler/rustc_target/src/callconv/mod.rs | 15 ++++++-- compiler/rustc_ty_utils/src/abi.rs | 12 +++++++ tests/ui/abi/c-zst.aarch64-darwin.stderr | 1 + tests/ui/abi/c-zst.powerpc-linux.stderr | 1 + tests/ui/abi/c-zst.s390x-linux.stderr | 1 + tests/ui/abi/c-zst.sparc64-linux.stderr | 1 + tests/ui/abi/c-zst.x86_64-linux.stderr | 1 + .../ui/abi/c-zst.x86_64-pc-windows-gnu.stderr | 1 + tests/ui/abi/debug.generic.stderr | 12 +++++++ tests/ui/abi/debug.loongarch64.stderr | 12 +++++++ tests/ui/abi/debug.riscv64.stderr | 12 +++++++ .../x86-64-sysv64-arg-ext.apple.stderr | 6 ++++ .../x86-64-sysv64-arg-ext.other.stderr | 6 ++++ tests/ui/abi/pass-indirectly-attr.stderr | 2 ++ tests/ui/abi/sysv64-zst.stderr | 1 + .../pass-by-value-abi.aarch64.stderr | 1 + .../c-variadic/pass-by-value-abi.win.stderr | 1 + .../pass-by-value-abi.x86_64.stderr | 3 ++ 29 files changed, 169 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 0bef86b1ae8c1..09cfd6b46af87 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1843,6 +1843,17 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn fptosi_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { self.fptoint_sat(true, val, dest_ty) } + + fn ptrauth_resign( + &mut self, + _value: Self::Value, + _old_key: u32, + _old_discriminator: u64, + _new_key: u32, + _new_discriminator: u64, + ) -> Self::Value { + bug!("Resigning of pointers not implemented"); + } } impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index e73b8aab54d73..edbd0ef7ad1c0 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -247,7 +247,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>, - _schema: Option<&PointerAuthSchema>, + _schema: Option, ) -> RValue<'gcc> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 0e3fa72fbcfb3..4194f7de4609f 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -401,7 +401,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - _pointer_auth_schema: Option<&PointerAuthSchema>, + _pointer_auth_schema: Option, ) -> RValue<'gcc> { let func_name = self.tcx.symbol_name(instance).name; diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index dfae4eceebe44..8e391ce1dd0e8 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -375,6 +375,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { fixed_count: 3, conv: CanonAbi::C, can_unwind: false, + ptrauth_type_discriminator: 0, }; fn_abi.adjust_for_foreign_abi(self.cx, ExternAbi::C { unwind: false }); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 408c258fa3475..8d3b1b8a75caa 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1543,6 +1543,30 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { let cold_inline = llvm::AttributeKind::Cold.create_attr(self.llcx); attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &[cold_inline]); } + + fn ptrauth_resign( + &mut self, + value: &'ll Value, + old_key: u32, + old_discriminator: u64, + new_key: u32, + new_discriminator: u64, + ) -> &'ll Value { + let ptr_as_int = self.ptrtoint(value, self.type_i64()); + let resigned_int = self.call_intrinsic( + "llvm.ptrauth.resign", + &[], + &[ + ptr_as_int, + self.const_i32(old_key as i32), + self.const_i64(old_discriminator as i64), + self.const_i32(new_key as i32), + self.const_i64(new_discriminator as i64), + ], + ); + + self.inttoptr(resigned_int, self.val_ty(value)) + } } impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { @@ -2060,8 +2084,14 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // bundles. // Once this is resolved, we should analyze each call and skip direct calls. See the // discussion in the rust-lang issue: - let key: u32 = 0; - let discriminator: u64 = 0; + + let key: u32 = self.sess().pointer_authentication_fn_ptr_key().unwrap() as u32; + let discriminator = if self.sess().pointer_authentication_fn_ptr_type_discrimination() { + fn_abi?.ptrauth_type_discriminator + } else { + 0 + }; + Some(llvm::OperandBundleBox::new( "ptrauth", &[self.const_u32(key), self.const_u64(discriminator)], diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 205e2e9a14701..0098d25345e5d 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -30,11 +30,9 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( cx: &CodegenCx<'ll, '_>, instance: Instance<'tcx>, llfn: &'ll llvm::Value, - schema: &PointerAuthSchema, + schema: PointerAuthSchema, ) -> &'ll llvm::Value { - if cx.tcx.sess.pointer_authentication_functions().is_none() { - return llfn; - } + assert!(cx.tcx.sess.pointer_authentication_functions().is_some()); // Only free functions or methods let def_id = instance.def_id(); @@ -317,7 +315,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { cv: Scalar, layout: abi::Scalar, llty: &'ll Type, - schema: Option<&PointerAuthSchema>, + schema: Option, ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 7ea30a5b4db6d..ecd020902ad9d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -913,7 +913,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - pointer_auth_schema: Option<&PointerAuthSchema>, + pointer_auth_schema: Option, ) -> &'ll Value { // When pointer authentication metadata is provided, `get_fn_addr` will // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 0d27ff90991b3..1f455221507be 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -667,4 +667,13 @@ pub trait BuilderMethods<'a, 'tcx>: fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn apply_attrs_to_cleanup_callsite(&mut self, llret: Self::Value); + + fn ptrauth_resign( + &mut self, + value: Self::Value, + old_key: u32, + old_discriminator: u64, + new_key: u32, + new_discriminator: u64, + ) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index b4eba38d39c19..ad5c4443f2dbc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -47,7 +47,7 @@ pub trait ConstCodegenMethods: BackendTypes { cv: Scalar, layout: abi::Scalar, llty: Self::Type, - schema: Option<&PointerAuthSchema>, + schema: Option, ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index add7128a2974b..6589cd219885b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -22,7 +22,7 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn get_fn_addr( &self, instance: Instance<'tcx>, - pointer_auth_schema: Option<&PointerAuthSchema>, + pointer_auth_schema: Option, ) -> Self::Value; fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index e35ff2e8cc76d..43b79307ada4e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -96,6 +96,7 @@ pub enum PointerAuthARM8_3Key { } /// Forms of extra discrimination. +#[derive(Clone, Debug, PartialEq)] pub enum PointerAuthDiscrimination { /// No additional discrimination. None, @@ -108,6 +109,7 @@ pub enum PointerAuthDiscrimination { } /// Types of address discrimination. +#[derive(Clone, Debug)] pub enum PointerAuthAddressDiscriminator { /// Enable/disable hardware address discrimination. HardwareAddress(bool), @@ -116,6 +118,7 @@ pub enum PointerAuthAddressDiscriminator { Synthetic(u64), } +#[derive(Clone, Debug)] pub struct PointerAuthSchema { pub is_address_discriminated: PointerAuthAddressDiscriminator, pub discrimination_kind: PointerAuthDiscrimination, @@ -1192,12 +1195,26 @@ impl Session { self.pointer_auth_config.is_some() } - pub fn pointer_authentication_functions(&self) -> Option<&PointerAuthSchema> { - self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()) + pub fn pointer_authentication_functions(&self) -> Option { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.clone()) } - pub fn pointer_authentication_init_fini(&self) -> Option<&PointerAuthSchema> { - self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()) + pub fn pointer_authentication_init_fini(&self) -> Option { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.clone()) + } + + pub fn pointer_authentication_fn_ptr_type_discrimination(&self) -> bool { + self.pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + .is_some_and(|schema| schema.discrimination_kind == PointerAuthDiscrimination::Type) + } + + pub fn pointer_authentication_fn_ptr_key(&self) -> Option { + self.pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + .map(|schema| schema.key) } } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 54f4ff77627b9..329ecfde0a11b 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -618,12 +618,22 @@ pub struct FnAbi<'a, Ty> { pub conv: CanonAbi, /// Indicates if an unwind may happen across a call to this function. pub can_unwind: bool, + /// Computed type discriminator for pointer authentication purpose. + pub ptrauth_type_discriminator: u64, } // Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl. impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let FnAbi { args, ret, c_variadic, fixed_count, conv, can_unwind } = self; + let FnAbi { + args, + ret, + c_variadic, + fixed_count, + conv, + can_unwind, + ptrauth_type_discriminator, + } = self; f.debug_struct("FnAbi") .field("args", args) .field("ret", ret) @@ -631,6 +641,7 @@ impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> { .field("fixed_count", fixed_count) .field("conv", conv) .field("can_unwind", can_unwind) + .field("ptrauth_type_discriminator", ptrauth_type_discriminator) .finish() } } @@ -930,6 +941,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(ArgAbi<'_, usize>, 56); - static_assert_size!(FnAbi<'_, usize>, 80); + static_assert_size!(FnAbi<'_, usize>, 88); // tidy-alphabetical-end } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 8873fb13efe03..19447dbfe6f7b 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -6,6 +6,9 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, find_attr}; use rustc_middle::bug; use rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs; +use rustc_middle::ptrauth::{ + build_fn_ptr_type_discriminator_input_from_sig, compute_fn_ptr_type_discriminator, +}; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind, @@ -637,6 +640,15 @@ fn fn_abi_new_uncached<'tcx>( determined_fn_def_id, sig.abi(), ), + ptrauth_type_discriminator: if tcx.sess.pointer_authentication_fn_ptr_type_discrimination() + { + compute_fn_ptr_type_discriminator( + tcx, + &build_fn_ptr_type_discriminator_input_from_sig(sig), + ) + } else { + 0 + }, }; fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi()); debug!("fn_abi_new_uncached = {:?}", fn_abi); diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index 6d2ac90c0c975..7981923585305 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -59,6 +59,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 6d2ac90c0c975..7981923585305 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -59,6 +59,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index 125150f2c2e3f..40ef1a7450def 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -121,6 +121,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -217,6 +218,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -295,6 +297,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -379,6 +382,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -451,6 +455,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -530,6 +535,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -603,6 +609,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -680,6 +687,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -752,6 +760,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -830,6 +839,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -902,6 +912,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -1007,6 +1018,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 5f05174c12760..b50e92966d95a 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -121,6 +121,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -217,6 +218,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -295,6 +297,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -379,6 +382,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -451,6 +455,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -530,6 +535,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -603,6 +609,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -680,6 +687,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -752,6 +760,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -830,6 +839,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -902,6 +912,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -1007,6 +1018,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 5f05174c12760..b50e92966d95a 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -121,6 +121,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -217,6 +218,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -295,6 +297,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -379,6 +382,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -451,6 +455,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -530,6 +535,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -603,6 +609,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -680,6 +687,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -752,6 +760,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -830,6 +839,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -902,6 +912,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -1007,6 +1018,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr index 02015d2a2e51f..f875a14411fd1 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr @@ -81,6 +81,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -170,6 +171,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -259,6 +261,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -348,6 +351,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -437,6 +441,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -526,6 +531,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr index 9bb2ab45d9841..7ea941662e9ca 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr @@ -81,6 +81,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -170,6 +171,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -259,6 +261,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -348,6 +351,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -437,6 +441,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -526,6 +531,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index 320840c8149f5..e02eed82ad85e 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -83,6 +83,7 @@ error: fn_abi_of(extern_c) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-indirectly-attr.rs:20:1 | @@ -174,6 +175,7 @@ error: fn_abi_of(extern_rust) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-indirectly-attr.rs:27:1 | diff --git a/tests/ui/abi/sysv64-zst.stderr b/tests/ui/abi/sysv64-zst.stderr index 82d3793c35328..7c375cb593a6c 100644 --- a/tests/ui/abi/sysv64-zst.stderr +++ b/tests/ui/abi/sysv64-zst.stderr @@ -61,6 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/sysv64-zst.rs:8:1 | diff --git a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr index 45edd7bc0e0ee..b470e4935f5f2 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:27:1 | diff --git a/tests/ui/c-variadic/pass-by-value-abi.win.stderr b/tests/ui/c-variadic/pass-by-value-abi.win.stderr index b8e3f699b30e8..8b2cb0773ae13 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.win.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.win.stderr @@ -73,6 +73,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:27:1 | diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr index 1e203b93e66b3..e6daafcbb2fd6 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:27:1 | @@ -150,6 +151,7 @@ error: fn_abi_of(take_va_list_sysv64) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:37:1 | @@ -230,6 +232,7 @@ error: fn_abi_of(take_va_list_win64) = FnAbi { Win64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:44:1 | From f8dd8cadfe3af24e8cdb2f7b7e471a957330e82d Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:16:40 +0000 Subject: [PATCH 07/12] [PAC] Enable support for FPTR_TYPE_DISCR in ABI Version Also remove error messages/tests that used to guarded it. --- compiler/rustc_session/src/errors.rs | 6 ------ compiler/rustc_session/src/session.rs | 19 +++++-------------- ...on_not_supported_pointer_authentication.rs | 12 ------------ ...ot_supported_pointer_authentication.stderr | 4 ---- 4 files changed, 5 insertions(+), 36 deletions(-) delete mode 100644 tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs delete mode 100644 tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index d1989609cefe2..20e7cb288ad5b 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -381,12 +381,6 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } -#[derive(Diagnostic)] -#[diag("function pointer type discrimination is not supported")] -pub(crate) struct PointerAuthenticationTypeDiscriminationNotSupportedForTarget<'a> { - pub(crate) target_triple: &'a TargetTuple, -} - #[derive(Diagnostic)] #[diag( "`-Z pointer-authentication` is not supported for target {$target_triple} and will be ignored" diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 43b79307ada4e..4c0db96323621 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -209,8 +209,7 @@ impl PointerAuthConfig { const GOT: u32 = 8; const GOTOS: u32 = 9; const TYPEINFO_VT_PTR_DISCR: u32 = 10; - // FIXME(jchlanda) We don't yet support function pointer type discrimination. - // const FPTR_TYPE_DISCR: u32 = 11; + const FPTR_TYPE_DISCR: u32 = 11; let pauth_abi_version: u32 = (u32::from(self.intrinsics) << INTRINSICS) | (u32::from(self.function_pointers.is_some()) << CALLS) @@ -228,7 +227,10 @@ impl PointerAuthConfig { })) << 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); + | (u32::from(self.typeinfo_vt_ptr_discrimination) << TYPEINFO_VT_PTR_DISCR) + | (u32::from(self.function_pointers.as_ref().is_some_and(|schema| { + matches!(schema.discrimination_kind, PointerAuthDiscrimination::Type) + })) << FPTR_TYPE_DISCR); pauth_abi_version } @@ -1445,17 +1447,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::LinkerPluginToWindowsNotSupported); } - if sess - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()) - .is_some_and(|schema| matches!(schema.discrimination_kind, PointerAuthDiscrimination::Type)) - { - sess.dcx().emit_err(errors::PointerAuthenticationTypeDiscriminationNotSupportedForTarget { - target_triple: &sess.opts.target_triple, - }); - } - if sess.target.cfg_abi != CfgAbi::Pauthtest && !sess.opts.unstable_opts.pointer_authentication.is_empty() { diff --git a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs deleted file mode 100644 index 6838e749fd333..0000000000000 --- a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ ignore-backends: gcc -//@ check-fail -//@ needs-llvm-components: aarch64 - -//@ compile-flags: -Zpointer-authentication=+function-pointer-type-discrimination --crate-type=lib --target aarch64-unknown-linux-pauthtest - -#![feature(no_core)] -#![no_std] -#![no_main] -#![no_core] - -//~? ERROR function pointer type discrimination is not supported diff --git a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr deleted file mode 100644 index c040b0cb61f66..0000000000000 --- a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr +++ /dev/null @@ -1,4 +0,0 @@ -error: function pointer type discrimination is not supported - -error: aborting due to 1 previous error - From 8f7a911a87cdc05c0ee8593c3773aa914e249cfb Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:59:12 +0000 Subject: [PATCH 08/12] Support type discriminators in static allocations The codegen now walks the layout of static initializer types to find extern "C" function pointer fields, computes their type discriminators, and applies those discriminators when emitting authenticated function pointer relocations. Also make sure that type discrimination is never applied to init/fini entries. --- compiler/rustc_codegen_llvm/src/common.rs | 2 + compiler/rustc_codegen_llvm/src/consts.rs | 184 +++++++++++++++++++++- 2 files changed, 182 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 0098d25345e5d..af9846e281038 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -350,6 +350,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { alloc.inner(), IsStatic::No, IsInitOrFini::No, + None, ); let alloc = alloc.inner(); let value = match alloc.mutability { @@ -387,6 +388,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { alloc.inner(), IsStatic::No, IsInitOrFini::No, + None, ); self.static_addr_of_impl(init, alloc.inner().align, None) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 20091d1b05be5..c268aafce8967 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -3,6 +3,7 @@ use std::ops::Range; use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::LangItem; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; @@ -13,8 +14,11 @@ use rustc_middle::mir::interpret::{ read_target_uint, }; use rustc_middle::mono::MonoItem; +use rustc_middle::ptrauth::{ + build_fn_ptr_type_discriminator_input_from_ty, compute_fn_ptr_type_discriminator, +}; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{self, Instance}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::Symbol; use rustc_target::spec::Arch; @@ -37,11 +41,155 @@ pub(crate) enum IsInitOrFini { Yes, No, } + +/// Maps offsets within a static allocation to the function pointer +/// discriminator that should be applied when authenticating the relocation +/// emitted at that offset. +/// +/// Offsets are relative to the beginning of the allocation. +pub(crate) struct FnPtrDiscriminatorAtOffset { + map: FxHashMap, +} + +/// Recursively walks a type layout and records the offsets of all extern "C" +/// function pointer fields together with their computed type discriminators. +/// +/// Traversal currently supports: +/// - direct function pointers +/// - transparent wrappers +/// - structs +/// - tuples +/// - arrays +/// +/// Offsets are accumulated relative to the containing object. +fn collect_fn_ptr_discriminators<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> FnPtrDiscriminatorAtOffset { + let mut map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map); + + FnPtrDiscriminatorAtOffset { map } +} + +fn collect_fn_ptr_discriminators_inner<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + base_offset: Size, + map: &mut FxHashMap, +) { + // Direct function pointer. + if let Some(input) = build_fn_ptr_type_discriminator_input_from_ty(tcx, ty) { + let discr = compute_fn_ptr_type_discriminator(tcx, &input); + if discr != 0 { + map.insert(base_offset, discr); + } + + return; + } + + match ty.kind() { + ty::Adt(def, args) if def.repr().transparent() => { + let variant = def.non_enum_variant(); + + let Some((_, field)) = variant.fields.iter_enumerated().next() else { + return; + }; + + let field_ty = tcx.normalize_erasing_regions(typing_env, field.ty(tcx, args)); + + collect_fn_ptr_discriminators_inner(tcx, typing_env, field_ty, base_offset, map); + } + ty::Adt(def, args) if def.is_struct() => { + let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { + return; + }; + + let variant = def.non_enum_variant(); + + for (idx, field_def) in variant.fields.iter_enumerated() { + let field_ty = tcx.normalize_erasing_regions(typing_env, field_def.ty(tcx, args)); + + let field_offset = layout.fields.offset(idx.into()); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + field_ty, + base_offset + field_offset, + map, + ); + } + } + ty::Tuple(fields) => { + let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { + return; + }; + + for (idx, field_ty) in fields.iter().enumerate() { + let field_offset = layout.fields.offset(idx); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + field_ty, + base_offset + field_offset, + map, + ); + } + } + ty::Array(elem_ty, len) => { + let count = match len.try_to_target_usize(tcx) { + Some(v) => v, + None => return, + }; + + let Ok(elem_layout) = tcx.layout_of(typing_env.as_query_input(*elem_ty)) else { + return; + }; + + let stride = elem_layout.size; + + // Collect discriminator of one element, so we don't have to recompute it for all the + // elements in the array. + let mut elem_map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + *elem_ty, + Size::ZERO, + &mut elem_map, + ); + + // SAFETY: We immediately collect into a Vec and sort by offset. + // The HashMap iteration order is irrelevant and must not affect determinism. + #[allow(rustc::potential_query_instability)] + let mut entries: Vec<(Size, u64)> = elem_map.into_iter().collect(); + entries.sort_unstable_by_key(|(offset, _)| *offset); + + // Replicate for every array slot. + for i in 0..count { + let elem_base = base_offset + stride * i; + + for (inner_offset, discr) in entries.iter().copied() { + map.insert(elem_base + inner_offset, discr); + } + } + } + _ => {} + } +} + pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, alloc: &Allocation, is_static: IsStatic, is_init_fini: IsInitOrFini, + fn_ptr_discriminators: Option<&FnPtrDiscriminatorAtOffset>, ) -> &'ll Value { // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be @@ -121,7 +269,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( as u64; let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); - let schema = if cx.sess().pointer_authentication() { + let mut schema = if cx.sess().pointer_authentication() { match is_init_fini { IsInitOrFini::Yes => cx.sess().pointer_authentication_init_fini(), IsInitOrFini::No => cx.sess().pointer_authentication_functions(), @@ -129,6 +277,16 @@ pub(crate) fn const_alloc_to_llvm<'ll>( } else { None }; + let discr = fn_ptr_discriminators + .as_ref() + .and_then(|m| m.map.get(&Size::from_bytes(offset as u64))); + + // Init/fini entries must not participate in function pointer type discrimination. + if let (Some(schema), Some(discr)) = (schema.as_mut(), discr) + && is_init_fini == IsInitOrFini::No + { + schema.constant_discriminator = *discr as u16; + } llvals.push(cx.scalar_to_backend_with_pac( InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx), Scalar::Initialized { @@ -160,6 +318,15 @@ fn codegen_static_initializer<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { + let fn_ptr_discriminators = if cx.sess().pointer_authentication_fn_ptr_type_discrimination() { + let instance = Instance::mono(cx.tcx, def_id); + let ty = instance.ty(cx.tcx, cx.typing_env()); + + Some(collect_fn_ptr_discriminators(cx.tcx, cx.typing_env(), ty)) + } else { + None + }; + let alloc = cx.tcx.eval_static_initializer(def_id)?; let attrs = cx.tcx.codegen_fn_attrs(def_id); // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion @@ -175,7 +342,16 @@ fn codegen_static_initializer<'ll, 'tcx>( } }) .unwrap_or(IsInitOrFini::No); - Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc)) + Ok(( + const_alloc_to_llvm( + cx, + alloc.inner(), + IsStatic::Yes, + is_in_init_fini, + fn_ptr_discriminators.as_ref(), + ), + alloc, + )) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -833,7 +1009,7 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the // same `ConstAllocation`? - let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); + let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No, None); let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind); // static_addr_of_impl returns the bare global variable, which might not be in the default From cde02669022759f77f37d4f5a0858005337b1a61 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 13:20:29 +0000 Subject: [PATCH 09/12] [PAC] Function pointer type discrimination for transmutes Implement pointer authentication domain handling for function pointer transmutes. When function pointer type discrimination is enabled, transmuting between function pointer types with different authentication domains now re-signs the pointer using the appropriate discriminator. --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 217 ++++++++++++++++++- 1 file changed, 205 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 24b66f11a953e..335b1c2f67c05 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,5 +1,8 @@ use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; +use rustc_middle::ptrauth::{ + build_fn_ptr_type_discriminator_input_from_ty, compute_fn_ptr_type_discriminator, +}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Mutability, Ty, TyCtxt}; @@ -14,7 +17,143 @@ use crate::common::{IntPredicate, TypeKind}; use crate::traits::*; use crate::{MemFlags, base}; +/// Type metadata used when applying pointer authentication semantics during +/// transmute lowering. +struct TransmuteInfo<'tcx> { + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx>, +} + impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + /// Applies pointer-authentication-aware semantic transmute, that is + /// ensuring that when a function pointer is transmuted between two types + /// that map to different authentication domains (discriminators), the + /// resulting pointer is re-signed appropriately. + /// + /// Only SSA `OperandValue::Immediate` values are eligible for this path. + fn codegen_semantic_transmute_operand( + &mut self, + bx: &mut Bx, + operand: OperandRef<'tcx, Bx::Value>, + cast: TyAndLayout<'tcx>, + ) -> OperandValue { + let val = self.codegen_transmute_operand(bx, operand, cast); + + let OperandValue::Immediate(ptr) = val else { + return val; + }; + + let info = TransmuteInfo { src_ty: operand.layout.ty, dst_ty: cast.ty }; + + OperandValue::Immediate(self.resign_transmuted_fn_ptr(bx, ptr, info)) + } + + /// Applies pointer-authentication domain correction for a function pointer + /// value being transmuted between two types. + /// + /// The "domain" is defined by the function pointer type discriminator. If + /// the source and destination types map to different discriminator values, + /// the pointer must be re-signed using `llvm.ptrauth.resign` intrinsic. + /// + /// A discriminator value of `0` is used to represent non-function-pointer + /// or "raw pointer domain" values. + /// ```text + // static mut CPTR: *const u8 = 0 as *const u8; + // ... = mem::transmute::<*const u8, unsafe extern "C" fn()>(CPTR); + /// ``` + /// where the source has no authentication domain. + fn resign_transmuted_fn_ptr( + &mut self, + bx: &mut Bx, + val: Bx::Value, + info: TransmuteInfo<'tcx>, + ) -> Bx::Value { + let tcx = bx.tcx(); + + let src_input = build_fn_ptr_type_discriminator_input_from_ty(tcx, info.src_ty); + let dst_input = build_fn_ptr_type_discriminator_input_from_ty(tcx, info.dst_ty); + + let src_disc = match src_input { + Some(src) => compute_fn_ptr_type_discriminator(tcx, &src), + None => 0, + }; + + let dst_disc = match dst_input { + Some(dst) => compute_fn_ptr_type_discriminator(tcx, &dst), + None => 0, + }; + + if src_disc == dst_disc { + return val; + } + + debug!("resign_transmuted_fn_ptr\t{:#x} -> {:#x}", src_disc, dst_disc); + + let key = self.cx.tcx().sess.pointer_authentication_fn_ptr_key().unwrap() as u32; + bx.ptrauth_resign(val, key, src_disc, key, dst_disc) + } + + /// Walks through `#[repr(transparent)]` wrappers to find an underlying + /// function pointer or function definition. + /// + /// Returns the corresponding layout if one is found, otherwise `None`. + fn transparent_fn_ptr_layout( + &self, + mut layout: TyAndLayout<'tcx>, + ) -> Option> { + loop { + match layout.ty.kind() { + ty::FnPtr(..) | ty::FnDef(..) => return Some(layout), + + ty::Adt(def, _) if def.repr().transparent() => { + layout = layout.field(self.cx, 0); + } + + _ => return None, + } + } + } + + /// Applies pointer-authentication domain change during a transmute into a + /// memory-backed place. + /// + /// Unlike the operand version, this path handles values stored in memory + /// and therefore must unwrap #[repr(transparent)] wrapper types so that pointer + /// authentication is based on the underlying function pointer type. + /// + /// Only immediate values are subject to ptrauth adjustment; other + /// representations are passed through unchanged. + fn codegen_semantic_transmute_place( + &mut self, + bx: &mut Bx, + src: OperandRef<'tcx, Bx::Value>, + dst: PlaceRef<'tcx, Bx::Value>, + ) { + debug!( + "codegen_semantic_transmute_place\tsrc={:?}, dst={:?}", + src.layout.ty.kind(), + dst.layout.ty.kind() + ); + + let info = TransmuteInfo { + src_ty: self.transparent_fn_ptr_layout(src.layout).map_or(src.layout.ty, |l| l.ty), + dst_ty: self.transparent_fn_ptr_layout(dst.layout).map_or(dst.layout.ty, |l| l.ty), + }; + + let dest = dst.val.with_type(src.layout); + + let val = match src.val { + OperandValue::Immediate(v) => { + let v = self.resign_transmuted_fn_ptr(bx, v, info); + OperandValue::Immediate(v) + } + other => other, + }; + + OperandRef { val, layout: src.layout, move_annotation: None } + .store_with_annotation(bx, dest); + } + #[instrument(level = "trace", skip(self, bx))] pub(crate) fn codegen_rvalue( &mut self, @@ -106,8 +245,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Cast( mir::CastKind::Transmute | mir::CastKind::Subtype, ref operand, - _ty, + ty, ) => { + if self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + let src_ty = operand.ty(self.mir, self.cx.tcx()); + let dst_ty = self.monomorphize(ty); + + if src_ty.is_fn_ptr() || dst_ty.is_fn_ptr() { + let op = self.codegen_operand(bx, operand); + let cast = bx.cx().layout_of(dst_ty); + + let val = self.codegen_semantic_transmute_operand(bx, op, cast); + + OperandRef { val, layout: cast, move_annotation: None } + .store_with_annotation(bx, dest); + + return; + } + } + let src = self.codegen_operand(bx, operand); self.codegen_transmute(bx, src, dest); } @@ -238,7 +394,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Since in this path we have a place anyway, we can store or copy to it, // making sure we use the destination place's alignment even if the // source would normally have a higher one. - src.store_with_annotation(bx, dst.val.with_type(src.layout)); + + if self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + self.codegen_semantic_transmute_place(bx, src, dst); + } else { + src.store_with_annotation(bx, dst.val.with_type(src.layout)); + } } } @@ -252,6 +413,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operand: OperandRef<'tcx, Bx::Value>, cast: TyAndLayout<'tcx>, ) -> OperandValue { + debug!( + "codegen_transmute_operand\t + from_ty={:?} to_ty={:?} from_layout={:?} to_layout={:?} is fnptr=({}, {})", + operand.layout.ty, + cast.ty, + operand.layout.backend_repr, + cast.backend_repr, + operand.layout.ty.is_fn_ptr(), + cast.ty.is_fn_ptr() + ); if let abi::BackendRepr::Memory { .. } = cast.backend_repr && !cast.is_zst() { @@ -437,12 +608,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ) .unwrap(); - OperandValue::Immediate( - bx.get_fn_addr( - instance, - bx.sess().pointer_authentication_functions(), - ), - ) + let mut schema = bx.sess().pointer_authentication_functions(); + + if let Some(ref mut s) = schema { + if bx.sess().pointer_authentication_fn_ptr_type_discrimination() { + if let Some(input) = build_fn_ptr_type_discriminator_input_from_ty( + bx.tcx(), + operand.layout.ty, + ) { + s.constant_discriminator = + compute_fn_ptr_type_discriminator( + bx.tcx(), + &input, + ) as u16; + } + } + } + + OperandValue::Immediate(bx.get_fn_addr(instance, schema)) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -457,9 +640,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ty::ClosureKind::FnOnce, ); OperandValue::Immediate( + // A closure coerced to a function pointer retains the Rust + // ABI. Pointer authentication only applies to extern + // "C"/System ABI function pointer, hence pass None to + // `get_fn_addr`. bx.cx().get_fn_addr( instance, - bx.sess().pointer_authentication_functions(), + None, ), ) } @@ -536,7 +723,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } mir::CastKind::Transmute | mir::CastKind::Subtype => { - self.codegen_transmute_operand(bx, operand, cast) + if self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + self.codegen_semantic_transmute_operand(bx, operand, cast) + } else { + self.codegen_transmute_operand(bx, operand, cast) + } } }; OperandRef { val, layout: cast, move_annotation: None } @@ -681,8 +872,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }; - let fn_ptr = - bx.get_fn_addr(instance, bx.sess().pointer_authentication_functions()); + // This is the address of a compiler-generated TLS shim function. It is not an + // externally visible function pointer and does not require function pointer + // authentication signing. + let fn_ptr = bx.get_fn_addr(instance, None); let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { From c3e6272b6244aa957e3db1ccdb23e85672048132 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 13:26:56 +0000 Subject: [PATCH 10/12] [PAC] Propagate function pointer type discrimination through `get_fn_addr` call sites Fill in function pointer type discriminators logic across remaining `get_fn_addr` call sites and explicitly avoid applying it where discrimination is not meaningful. Some uses of `get_fn_addr` are intentionally left unsigned, including the EH personality function, entry wrappers, and compiler-generated Rust ABI shims. --- compiler/rustc_codegen_llvm/src/context.rs | 12 +++- compiler/rustc_codegen_ssa/src/base.rs | 11 ++-- compiler/rustc_codegen_ssa/src/common.rs | 21 +++++-- compiler/rustc_codegen_ssa/src/mir/block.rs | 63 ++++++++++++++++----- 4 files changed, 84 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index ecd020902ad9d..f77cc011ae45d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -973,6 +973,16 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { + // We intentionally do NOT apply pointer authentication (and/or function type + // discriminators to the EH personality function). + // + // Although `get_fn_addr` normally produces a signed function pointer for + // externally-callable functions, the EH personality is not an indirect call + // target in the SSA sense. Instead, it is a compile-time constant attached to + // the Function object (via LLVM's `setPersonalityFn`) and consumed only by + // exception handling metadata generation (landing pads / unwind tables). + // LLVM never loads or invokes the personality via a function pointer value; + // it is not part of the program's call graph or data flow. Some(def_id) if name.is_none() => self.get_fn_addr( ty::Instance::expect_resolve( tcx, @@ -981,7 +991,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty::List::empty(), DUMMY_SP, ), - tcx.sess.pointer_authentication_functions(), + None, ), _ => { let name = name.unwrap_or("rust_eh_personality"); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 0711fbb72ee67..2734b8834ac42 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -492,8 +492,11 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // We want to create the wrapper only when the codegen unit is the primary one return None; } - - let main_llfn = cx.get_fn_addr(instance, cx.sess().pointer_authentication_functions()); + // No function pointer signing / type discriminator is needed here. Although `get_fn_addr` is + // used to obtain function pointers, both the user's `main` and `LangItem::Start` use the Rust + // ABI (currently pointer authentication is only supported for C/System ABI). The same applies + // to the logic in `create_entry_fn` further below. + let main_llfn = cx.get_fn_addr(instance, None); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); @@ -554,8 +557,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.tcx().mk_args(&[main_ret_ty.into()]), DUMMY_SP, ); - let start_fn = - cx.get_fn_addr(start_instance, cx.sess().pointer_authentication_functions()); + + let start_fn = cx.get_fn_addr(start_instance, None); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index ae72258a87c86..c42343e89a123 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -2,6 +2,9 @@ use rustc_hir::LangItem; use rustc_hir::attrs::PeImportNameType; +use rustc_middle::ptrauth::{ + build_fn_ptr_type_discriminator_input_from_instance, compute_fn_ptr_type_discriminator, +}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; @@ -117,11 +120,19 @@ pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - ( - bx.fn_abi_of_instance(instance, ty::List::empty()), - bx.get_fn_addr(instance, tcx.sess.pointer_authentication_functions()), - instance, - ) + let mut schema = bx.sess().pointer_authentication_functions().clone(); + + if let Some(ref mut s) = schema + && bx.sess().pointer_authentication_fn_ptr_type_discrimination() + { + // It is unlikely that any of LangItem will follow the extern C/System ABI, but it future + // proofs the implementation. + let disc_input = build_fn_ptr_type_discriminator_input_from_instance(tcx, instance); + let disc = compute_fn_ptr_type_discriminator(tcx, &disc_input) as u16; + + s.constant_discriminator = disc; + } + (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance, schema), instance) } pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index ab9972e4eea2c..bda67f51bd7c6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -11,6 +11,9 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::lang_items::LangItem; use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; +use rustc_middle::ptrauth::{ + build_fn_ptr_type_discriminator_input_from_instance, compute_fn_ptr_type_discriminator, +}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; @@ -684,12 +687,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { virtual_drop, ) } - _ => ( - false, - bx.get_fn_addr(drop_fn, bx.sess().pointer_authentication_functions()), - bx.fn_abi_of_instance(drop_fn, ty::List::empty()), - drop_fn, - ), + _ => { + let mut schema = bx.sess().pointer_authentication_functions().clone(); + + if let Some(ref mut s) = schema + && bx.sess().pointer_authentication_fn_ptr_type_discrimination() + { + let disc_input = + build_fn_ptr_type_discriminator_input_from_instance(bx.tcx(), drop_fn); + let disc = compute_fn_ptr_type_discriminator(bx.tcx(), &disc_input) as u16; + + s.constant_discriminator = disc; + } + ( + false, + bx.get_fn_addr(drop_fn, schema), + bx.fn_abi_of_instance(drop_fn, ty::List::empty()), + drop_fn, + ) + } }; // We generate a null check for the drop_fn. This saves a bunch of relocations being @@ -1102,13 +1118,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) .unwrap(); - ( - None, - Some(bx.get_fn_addr( + let mut schema = bx.sess().pointer_authentication_functions().clone(); + + if let Some(ref mut s) = schema + && bx.sess().pointer_authentication_fn_ptr_type_discrimination() + { + let disc_input = build_fn_ptr_type_discriminator_input_from_instance( + bx.tcx(), instance, - bx.sess().pointer_authentication_functions(), - )), - ) + ); + let disc = + compute_fn_ptr_type_discriminator(bx.tcx(), &disc_input) as u16; + + s.constant_discriminator = disc; + }; + + (None, Some(bx.get_fn_addr(instance, schema))) } _ => (Some(instance), None), } @@ -1422,7 +1447,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let fn_ptr = match (instance, llfn) { (Some(instance), None) => { - bx.get_fn_addr(instance, bx.sess().pointer_authentication_functions()) + let mut schema = bx.sess().pointer_authentication_functions().clone(); + + if let Some(ref mut s) = schema + && bx.sess().pointer_authentication_fn_ptr_type_discrimination() + { + let disc_input = + build_fn_ptr_type_discriminator_input_from_instance(bx.tcx(), instance); + let disc = compute_fn_ptr_type_discriminator(bx.tcx(), &disc_input) as u16; + + s.constant_discriminator = disc; + } + + bx.get_fn_addr(instance, schema) } (_, Some(llfn)) => llfn, _ => span_bug!(fn_span, "no instance or llfn for call"), From 54447d299d47c3a01d46effd81f8811b581692a7 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 13:36:07 +0000 Subject: [PATCH 11/12] [PAC] Minicore updates to support fn ptr type discriminator tests --- tests/auxiliary/minicore.rs | 54 ++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index e50b9b785ad48..8bfb4f9d79911 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -112,6 +112,7 @@ impl Copy for [T; N] {} pub struct PhantomData; impl Copy for PhantomData {} +#[rustc_diagnostic_item = "Option"] pub enum Option { None, Some(T), @@ -249,11 +250,18 @@ pub trait Add { fn add(self, _: Rhs) -> Self::Output; } +// Avoid needing to add all of the overflow handling and panic language items impl Add for isize { type Output = isize; fn add(self, other: isize) -> isize { - 7 // avoid needing to add all of the overflow handling and panic language items + 7 + } +} +impl Add for i32 { + type Output = i32; + fn add(self, rhs: i32) -> i32 { + 7 } } @@ -300,18 +308,30 @@ impl_marker_trait!( ); impl Sync for () {} - impl Sync for [T; N] {} +impl Sync for Option {} +impl Sync for &T {} + // Function pointers are treated as `Sync` to match real `core` behavior. // // Minicore provides only the minimal set of impls required by tests. Rather // than exhaustively covering all possible function pointer signatures, -// additional impls should be added as needed. -impl Sync for fn() -> R {} -impl Sync for extern "C" fn() -> R {} -impl Sync for unsafe extern "C" fn() -> R {} -impl Sync for extern "C" fn(A) -> R {} -impl Sync for unsafe extern "C" fn(A) -> R {} +// additional arities should be added as needed. +macro_rules! impl_sync_for_fn_ptrs { + ($(($($T:ident),*)),* $(,)?) => { + $( + impl<$($T,)* R> Sync for fn($($T),*) -> R {} + + impl<$($T,)* R> Sync for extern "C" fn($($T),*) -> R {} + impl<$($T,)* R> Sync for unsafe extern "C" fn($($T),*) -> R {} + + impl<$($T,)* R> Sync for extern "C" fn($($T,)* ...) -> R {} + impl<$($T,)* R> Sync for unsafe extern "C" fn($($T,)* ...) -> R {} + )* + }; +} + +impl_sync_for_fn_ptrs!((), (A), (A, B), (A, B, C), (A, B, C, D),); #[lang = "drop_glue"] fn drop_glue(_: &mut T) {} @@ -348,7 +368,7 @@ pub trait CoerceUnsized {} impl<'a, 'b: 'a, T: PointeeSized + Unsize, U: PointeeSized> CoerceUnsized<&'a U> for &'b T {} #[lang = "drop"] -trait Drop { +pub trait Drop { fn drop(&mut self); } @@ -359,7 +379,7 @@ pub const unsafe fn copy_nonoverlapping(src: *const T, dst: *mut T, count: us pub mod mem { #[rustc_nounwind] #[rustc_intrinsic] - pub unsafe fn transmute(src: Src) -> Dst; + pub const unsafe fn transmute(src: Src) -> Dst; #[rustc_nounwind] #[rustc_intrinsic] @@ -378,6 +398,15 @@ pub mod ptr { unsafe { volatile_store(dst, src) }; } + + #[inline] + #[rustc_diagnostic_item = "ptr_read_volatile"] + pub unsafe fn read_volatile(src: *const T) -> T { + #[rustc_intrinsic] + pub unsafe fn volatile_load(src: *const T) -> T; + + unsafe { volatile_load(src) } + } } pub mod hint { @@ -392,9 +421,10 @@ pub mod hint { #[lang = "c_void"] #[repr(u8)] +#[allow(non_camel_case_types)] pub enum c_void { - __variant1, - __variant2, + Variant1, + Variant2, } #[rustc_builtin_macro(pattern_type)] From f23257c4249256dc234ec18129dc46ac6d7ec006 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 13:58:20 +0000 Subject: [PATCH 12/12] [PAC] Function pointer type discrimination tests And update to the main pauthtest document: * list new tests * remove the need for patching `libc` as the changes to it already went in. Unfortunately `cc-rs` is held back by `compiler/rustc_llvm/Cargo.toml` which pins to an old version: `cc = "=1.2.16"` --- .../aarch64-unknown-linux-pauthtest.md | 49 +- .../pauth/pauth-attr-special-funcs.rs | 5 +- ...n-ptr-type-discrimination-deeply-nested.rs | 150 +++++ ...auth-fn-ptr-type-discrimination-encoder.rs | 573 ++++++++++++++++++ ...-type-discrimination-fn-ptr-return-type.rs | 61 ++ ...ptr-type-discrimination-option-callback.rs | 86 +++ ...n-ptr-type-discrimination-option-return.rs | 62 ++ ...pauth-fn-ptr-type-discrimination-option.rs | 48 ++ ...fn-ptr-type-discrimination-running-test.rs | 304 ++++++++++ ...h-fn-ptr-type-discrimination-rust-array.rs | 111 ++++ .../pauth-fn-ptr-type-discrimination-simd.rs | 208 +++++++ ...-ptr-type-discrimination-struct-members.rs | 529 ++++++++++++++++ ...-fn-ptr-type-discrimination-struct-name.rs | 100 +++ tests/codegen-llvm/pauth/pauth-init-fini.rs | 12 +- .../before_instcombine.check | 4 + .../before_instcombine_ty_disc.check | 4 + .../pauth-drop-terminator/full_ir.check | 3 + tests/run-make/pauth-drop-terminator/main.rs | 22 + tests/run-make/pauth-drop-terminator/rmake.rs | 61 ++ 19 files changed, 2370 insertions(+), 22 deletions(-) create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-deeply-nested.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-encoder.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-fn-ptr-return-type.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-callback.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-return.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-running-test.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-rust-array.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-simd.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-members.rs create mode 100644 tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-name.rs create mode 100644 tests/run-make/pauth-drop-terminator/before_instcombine.check create mode 100644 tests/run-make/pauth-drop-terminator/before_instcombine_ty_disc.check create mode 100644 tests/run-make/pauth-drop-terminator/full_ir.check create mode 100644 tests/run-make/pauth-drop-terminator/main.rs create mode 100644 tests/run-make/pauth-drop-terminator/rmake.rs diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md index e0f11c7800dcd..d4cb6ed97acb5 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -109,29 +109,18 @@ Clang-based toolchain. In this case, no wrapper script is required, Introduction of `aarch64-unknown-linux-pauthtest` target needs to be propagated to various crates/repos, so that they can correctly recognise and handle it. -Specifically: +At the time of writing this document the following requires patching: * `cc-rs`: https://github.com/jchlanda/cc-rs/tree/jakub/cc-v1.2.28-pauthtest -* `libc`: https://github.com/jchlanda/libc/tree/jakub/0.2.183-pauthtest * `backtrace`: https://github.com/jchlanda/backtrace-rs/tree/jakub/backtrace-v0.3.76-pauthtest -The patched versions of `cc-rs` and `libc` will have to be registered through -`[patch.crates-io]` section of `Cargo.toml` files both in: -`/src/bootstrap/` and `/library/`. Check out `cc-rs` and -`libc` to `/patches` and update config files. See attached diff for -details: +The patched versions of `cc-rs` will have to be registered through +`[patch.crates-io]` section of `Cargo.toml` file in: +`/src/bootstrap/`. Check out `cc-rs` to `/patches` and +update config file. See attached diff for details:
```diff -diff --git a/library/Cargo.toml b/library/Cargo.toml -index e30e6240942..fb5a12f0065 100644 ---- a/library/Cargo.toml -+++ b/library/Cargo.toml -@@ -59,3 +59,4 @@ rustflags = ["-Cpanic=abort"] - rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } - rustc-std-workspace-alloc = { path = 'rustc-std-workspace-alloc' } - rustc-std-workspace-std = { path = 'rustc-std-workspace-std' } -+libc = { path = '/patches/libc' } diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index e1725db60cf..46763cdf9a4 100644 --- a/src/bootstrap/Cargo.toml @@ -147,7 +136,7 @@ index e1725db60cf..46763cdf9a4 100644
-In contrast to `cc-rs` and `libc`, which are external crates resolved from +In contrast to `cc-rs`, which is an external crate resolved from [crates.io](https://crates.io/) and can be overridden using `[patch.crates-io]`, `backtrace` is included in the Rust repository as a git submodule under `/library/backtrace`. At the time of writing, the necessary change @@ -464,6 +453,18 @@ The following categories are supported (all present in tree): * pauth-extern-weak-global.rs * pauth-init-fini.rs * pauth-attr-special-funcs.rs + * pauth-fn-ptr-type-discrimination-deeply-nested.rs + * pauth-fn-ptr-type-discrimination-encoder.rs + * pauth-fn-ptr-type-discrimination-fn-ptr-return-type.rs + * pauth-fn-ptr-type-discrimination-option-callback.rs + * pauth-fn-ptr-type-discrimination-option-return.rs + * pauth-fn-ptr-type-discrimination-option.rs + * pauth-fn-ptr-type-discrimination-running-test.rs + * pauth-fn-ptr-type-discrimination-rust-array.rs + * pauth-fn-ptr-type-discrimination-simd.rs + * pauth-fn-ptr-type-discrimination-struct-members.rs + * pauth-fn-ptr-type-discrimination-struct-name.rs + * pauth-drop-terminator (implemented in run-make) * End-to-end execution tests * Rust-driven quicksort (pauth-quicksort-rust-driver) * C-driven quicksort (pauth-quicksort-c-driver) @@ -472,7 +473,6 @@ The following categories are supported (all present in tree): * pauth-static-link-warning * enable_pointer_authentication_validation.rs * invalid_target_pointer_authentication.rs - * type_discrimination_not_supported_pointer_authentication.rs * incompatible_pauth.rs All tests from `assembly-llvm`, `codegen-llvm`, `codegen-units`, `coverage`, @@ -493,13 +493,24 @@ x.py test --target aarch64-unknown-linux-pauthtest --force-rerun assembly-llvm \ tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs \ tests/codegen-llvm/pauth/pauth-extern-weak-global.rs \ tests/codegen-llvm/pauth/pauth-init-fini.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-deeply-nested.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-encoder.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-fn-ptr-return-type.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-callback.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-return.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-running-test.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-rust-array.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-simd.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-members.rs \ + tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-name.rs \ tests/run-make/pauth-quicksort-rust-driver \ tests/run-make/pauth-quicksort-c-driver \ tests/run-make/pauth-static-link-warning \ + tests/run-make/pauth-drop-terminator \ tests/ui/statics/crt-static-pauthtest.rs \ 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/target_modifiers/incompatible_pauth.rs ``` diff --git a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs index 2751494b9de7a..c704a86394d73 100644 --- a/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -8,7 +8,10 @@ use std::panic; -// CHECK: define {{.*}} @__rust_try{{.*}} [[ATTR_TRY:#[0-9]+]] +// Make sure that `rust_eh_personality` is not signed. +// CHECK: define internal i32 @{{.*}}lang_start{{.*}}pauth_attr_special_funcs(ptr %{{.*}}) unnamed_addr #[[#]] personality ptr @rust_eh_personality + +// CHECK: define {{.*}} @__rust_try{{.*}} [[ATTR_TRY:#[0-9]+]] personality ptr @rust_eh_personality { // CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]] // CHECK: attributes [[ATTR_TRY]] = { {{.*}}"aarch64-jump-table-hardening" diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-deeply-nested.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-deeply-nested.rs new file mode 100644 index 0000000000000..ff4a6e3197595 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-deeply-nested.rs @@ -0,0 +1,150 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. +//@ revisions: DISC NO_DISC + +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// Make sure that the compiler can see through chains of nested structs, both when used as globals, +// arguments and returns. +// +// Equivalent C: +// +// #include +// +// typedef int (*L0)(int); +// typedef int (*L1)(L0); +// typedef int (*L2)(L1); +// typedef int (*L3)(L2); +// typedef int (*L4)(L3); +// typedef L0 (*DeepRet)(void); +// +// int callback_i32(int x) { return x + 1; } +// +// int dummy_l1(L0 cb) { return cb(5); } +// int dummy_l2(L1 cb) { return cb(callback_i32); } +// int dummy_l3(L2 cb) { return cb(dummy_l1); } +// int dummy_l4(L3 cb) { return cb(dummy_l2); } +// +// L0 returned_fn(void) { return callback_i32; } +// +// DeepRet f_deep(L4 cb) { +// cb(dummy_l3); +// return returned_fn; +// } +// +// DeepRet (*T_DEEP)(L4) = f_deep; +// +// int main(void) { +// DeepRet ret_fn; +// L0 inner_fn; +// int result; +// +// ret_fn = T_DEEP(dummy_l4); +// inner_fn = ret_fn(); +// result = inner_fn(42); +// +// printf("result = %d\n", result); +// +// return 0; +// } + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] +extern crate minicore; +use minicore::hint::black_box; + +// Nested fn ptr chain. +type L0 = extern "C" fn(i32) -> i32; +type L1 = extern "C" fn(L0) -> i32; +type L2 = extern "C" fn(L1) -> i32; +type L3 = extern "C" fn(L2) -> i32; +type L4 = extern "C" fn(L3) -> i32; +// Function returning fn ptr. +type DeepRet = extern "C" fn() -> L0; + +#[used] +// DISC: @{{.*}}T_DEEP = constant ptr ptrauth (ptr @{{.*}}f_deep, i32 0, i64 1059), align 8 +// NO_DISC: @{{.*}}T_DEEP = constant ptr ptrauth (ptr @{{.*}}f_deep, i32 0), align 8 +static T_DEEP: unsafe extern "C" fn(L4) -> DeepRet = f_deep; + +// Leaf callback. +// CHECK-LABEL: callback_i32 +pub extern "C" fn callback_i32(x: i32) -> i32 { + x +} + +// Dummy chain impl. +// CHECK-LABEL: dummy_l1 +// CHECK: (ptr [[CB:%.*]]) +pub extern "C" fn dummy_l1(cb: L0) -> i32 { + // DISC: call i32 [[CB]](i32 5) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // NO_DISC: call i32 [[CB]](i32 5) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + cb(5) +} +// CHECK-LABEL: dummy_l2 +// CHECK: (ptr [[CB:%.*]]) +pub extern "C" fn dummy_l2(cb: L1) -> i32 { + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}callback_i32, i32 0, i64 2981)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}callback_i32, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + cb(callback_i32) +} +// CHECK-LABEL: dummy_l3 +// CHECK: (ptr [[CB:%.*]]) +pub extern "C" fn dummy_l3(cb: L2) -> i32 { + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l1, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l1, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + cb(dummy_l1) +} +// CHECK-LABEL: dummy_l4 +// CHECK: (ptr [[CB:%.*]]) +pub extern "C" fn dummy_l4(cb: L3) -> i32 { + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l2, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l2, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + cb(dummy_l2) +} +// Return fn impl. +// CHECK-LABEL: returned_fn +pub extern "C" fn returned_fn() -> L0 { + // DISC: ret ptr ptrauth (ptr @{{.*}}callback_i32, i32 0, i64 2981) + // NO_DISC: ret ptr ptrauth (ptr @{{.*}}callback_i32, i32 0) + return callback_i32; +} +// Entry point to the chain, takes L4 and returns function returning fn ptr. +// CHECK-LABEL: f_deep +// CHECK: (ptr [[CB:%.*]]) +pub extern "C" fn f_deep(cb: L4) -> DeepRet { + // DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l3, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC: call i32 [[CB]](ptr ptrauth (ptr @{{.*}}dummy_l3, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + cb(dummy_l3); + // DISC: ret ptr ptrauth (ptr @{{.*}}returned_fn, i32 0, i64 34128) + // NO_DISC: ret ptr ptrauth (ptr @{{.*}}returned_fn, i32 0) + return returned_fn; +} + +// CHECK-LABEL: main +pub fn main() { + unsafe { + // DISC: [[RET_FN:%.*]] = call ptr ptrauth (ptr @{{.*}}f_deep, i32 0, i64 1059)(ptr ptrauth (ptr @{{.*}}dummy_l4, i32 0, i64 12410)) {{.*}} [ "ptrauth"(i32 0, i64 1059) ] + // NO_DISC: [[RET_FN:%.*]] = call ptr ptrauth (ptr @{{.*}}f_deep, i32 0)(ptr ptrauth (ptr @{{.*}}dummy_l4, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let ret_fn: DeepRet = T_DEEP(dummy_l4); + // DISC: [[INNER_FN:%.*]] = call ptr [[RET_FN]]() {{.*}} [ "ptrauth"(i32 0, i64 34128) ] + // NO_DISC: [[INNER_FN:%.*]] = call ptr [[RET_FN]]() {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let inner_fn: L0 = ret_fn(); + // DISC: call i32 [[INNER_FN]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // NO_DISC: call i32 [[INNER_FN]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let result = inner_fn(42); + + black_box(result); + } +} diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-encoder.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-encoder.rs new file mode 100644 index 0000000000000..848dd4fe414e9 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-encoder.rs @@ -0,0 +1,573 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// The `encode_ty` function in is responsible +// for converting types to the literal values that are then used as the basis for hashing. Its +// implementation is a faithful translation of Clang's `encodeTypeForFunctionPointerAuth`. + +#![feature(repr_simd)] +#![feature(simd_ffi)] +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::{c_void, mem}; + +// Builtin types. +extern "C" { + fn f_i32(x: i32) -> i32; + fn f_f(x: f32) -> f32; + fn f_d(x: f64) -> f64; + fn f_2d(x: f64, y: f64) -> f64; + fn f_ld(x: f64) -> f64; + fn f_v() -> (); +} +type fn_i32 = unsafe extern "C" fn(i32) -> i32; +type fn_f = unsafe extern "C" fn(f32) -> f32; +type fn_d = unsafe extern "C" fn(f64) -> f64; +type fn_2d = unsafe extern "C" fn(f64, f64) -> f64; +type fn_v = unsafe extern "C" fn() -> (); +// discriminator: 2981 (0x0BA5), encoding: FiiE +// DISC: @{{.*}}T_I32 = constant ptr ptrauth (ptr @f_i32, i32 0, i64 2981), align 8 +// NO_DISC: @{{.*}}T_I32 = constant ptr ptrauth (ptr @f_i32, i32 0), align 8 +#[used] +static T_I32: fn_i32 = f_i32; +// discriminator: 28450 (0x6F22), encoding: FffE +// DISC: @{{.*}}T_F = constant ptr ptrauth (ptr @f_f, i32 0, i64 28450), align 8 +// NO_DISC: @{{.*}}T_F = constant ptr ptrauth (ptr @f_f, i32 0), align 8 +#[used] +static T_F: fn_f = f_f; +// discriminator: 43115 (0xA86B), encoding: FddE +// DISC: @{{.*}}T_D = constant ptr ptrauth (ptr @f_d, i32 0, i64 43115), align 8 +// NO_DISC: @{{.*}}T_D = constant ptr ptrauth (ptr @f_d, i32 0), align 8 +#[used] +static T_D: fn_d = f_d; +// discriminator: 38695 (0x9727), encoding: FdddE +// DISC: @{{.*}}T_2D = constant ptr ptrauth (ptr @f_2d, i32 0, i64 38695), align 8 +// NO_DISC: @{{.*}}T_2D = constant ptr ptrauth (ptr @f_2d, i32 0), align 8 +#[used] +static T_2D: fn_2d = f_2d; +// discriminator: 18983 (0x4A27), encoding: FvE +// DISC: @{{.*}}T_V = constant ptr ptrauth (ptr @f_v, i32 0, i64 18983), align 8 +// NO_DISC: @{{.*}}T_V = constant ptr ptrauth (ptr @f_v, i32 0), align 8 +#[used] +static T_V: fn_v = f_v; + +// Pointer types. +extern "C" { + fn f_ptr(x: *mut i32) -> i32; +} +type fn_ptr = unsafe extern "C" fn(*mut i32) -> i32; +// discriminator: 12410 (0x307A), encoding: FiPE +// DISC: @{{.*}}T_PTR = constant ptr ptrauth (ptr @f_ptr, i32 0, i64 12410), align 8 +// NO_DISC: @{{.*}}T_PTR = constant ptr ptrauth (ptr @f_ptr, i32 0), align 8 +#[used] +static T_PTR: fn_ptr = f_ptr; + +// Array types. +extern "C" { + fn f_arr_2(x: *mut i32) -> i32; + fn f_arr_4(x: *mut i32) -> i32; + fn f_arr2(x: *mut i32) -> i32; +} +type fn_arr_2 = unsafe extern "C" fn(*mut i32) -> i32; +type fn_arr_4 = unsafe extern "C" fn(*mut i32) -> i32; +type fn_arr2 = unsafe extern "C" fn(*mut i32) -> i32; +// discriminator: 12410 (0x307A), encoding: FiPE +// DISC: @{{.*}}T_ARR_2 = constant ptr ptrauth (ptr @f_arr_2, i32 0, i64 12410), align 8 +// NO_DISC: @{{.*}}T_ARR_2 = constant ptr ptrauth (ptr @f_arr_2, i32 0), align 8 +#[used] +static T_ARR_2: fn_arr_2 = f_arr_2; +// discriminator: 12410 (0x307A), encoding: FiPE +// DISC: @{{.*}}T_ARR_4 = constant ptr ptrauth (ptr @f_arr_4, i32 0, i64 12410), align 8 +// NO_DISC: @{{.*}}T_ARR_4 = constant ptr ptrauth (ptr @f_arr_4, i32 0), align 8 +#[used] +static T_ARR_4: fn_arr_4 = f_arr_4; +// discriminator: 12410 (0x307A), encoding: FiPE +// DISC: @{{.*}}T_ARR2 = constant ptr ptrauth (ptr @f_arr2, i32 0, i64 12410), align 8 +// NO_DISC: @{{.*}}T_ARR2 = constant ptr ptrauth (ptr @f_arr2, i32 0), align 8 +#[used] +static T_ARR2: fn_arr2 = f_arr2; + +// Complex types. +extern "C" { + fn f_cf(x: (f32, f32)) -> (f32, f32); + fn f_cd(x: (f64, f64)) -> (f64, f64); + // RUST does not have built int long double +} +type fn_cf = unsafe extern "C" fn((f32, f32)) -> (f32, f32); +type fn_cd = unsafe extern "C" fn((f64, f64)) -> (f64, f64); +// discriminator: 19255 (0x4B37), encoding: FCfCfE +// DISC: @{{.*}}T_CF = constant ptr ptrauth (ptr @f_cf, i32 0, i64 19255), align 8 +// NO_DISC: @{{.*}}T_CF = constant ptr ptrauth (ptr @f_cf, i32 0), align 8 +#[used] +static T_CF: fn_cf = f_cf; +// discriminator: 2553 (0x09F9), encoding: FCdCdE +// DISC: @{{.*}}T_CD = constant ptr ptrauth (ptr @f_cd, i32 0, i64 2553), align 8 +// NO_DISC: @{{.*}}T_CD = constant ptr ptrauth (ptr @f_cd, i32 0), align 8 +#[used] +static T_CD: fn_cd = f_cd; + +// Function types. +extern "C" { + fn f_nested(g: extern "C" fn(i32) -> i32, x: i32) -> i32; +} +type fn_i32_i32 = extern "C" fn(i32) -> i32; +type fn_nested = unsafe extern "C" fn(fn_i32_i32, i32) -> i32; +// discriminator: 20679 (0x50C7), encoding: FiPiE +// DISC: @{{.*}}T_NESTED = constant ptr ptrauth (ptr @f_nested, i32 0, i64 20679), align 8 +// NO_DISC: @{{.*}}T_NESTED = constant ptr ptrauth (ptr @f_nested, i32 0), align 8 +#[used] +static T_NESTED: fn_nested = f_nested; + +// Variadic function. +extern "C" { + fn f_var(x: i32, ...) -> i32; +} +type fn_var = unsafe extern "C" fn(i32, ...) -> i32; +// discriminator: 7476 (0x1D34), encoding: FiizE +// DISC: @{{.*}}T_VAR = constant ptr ptrauth (ptr @f_var, i32 0, i64 7476), align 8 +// NO_DISC: @{{.*}}T_VAR = constant ptr ptrauth (ptr @f_var, i32 0), align 8 +#[used] +static T_VAR: fn_var = f_var; + +// Enum coercion to int. +#[repr(i32)] +enum MyEnum { + A = 1, + B = 2, +} +extern "C" { + fn f_enum(x: MyEnum) -> MyEnum; +} +type fn_enum = unsafe extern "C" fn(MyEnum) -> MyEnum; +// discriminator: 2981 (0x0BA5), encoding: FiiE +// DISC: @{{.*}}T_ENUM = constant ptr ptrauth (ptr @f_enum, i32 0, i64 2981), align 8 +// NO_DISC: @{{.*}}T_ENUM = constant ptr ptrauth (ptr @f_enum, i32 0), align 8 +#[used] +static T_ENUM: fn_enum = f_enum; + +// Struct types. +#[repr(C)] +struct MyStruct { + x: i32, +} +extern "C" { + fn f_struct(x: MyStruct) -> MyStruct; +} +type fn_struct = unsafe extern "C" fn(MyStruct) -> MyStruct; +// discriminator: 17754 (0x455A), encoding: F8MyStruct8MyStructE +// DISC: @{{.*}}T_STRUCT = constant ptr ptrauth (ptr @f_struct, i32 0, i64 17754), align 8 +// NO_DISC: @{{.*}}T_STRUCT = constant ptr ptrauth (ptr @f_struct, i32 0), align 8 +#[used] +static T_STRUCT: fn_struct = f_struct; + +// Function pointer as arguments. +extern "C" { + fn f_fp(h: extern "C" fn(i32) -> i32) -> i32; +} +type fn_i32_i32_fp_as_arg = extern "C" fn(i32) -> i32; +type fn_fp = unsafe extern "C" fn(fn_i32_i32_fp_as_arg) -> i32; +// discriminator: 12410 (0x307A), encoding: FiPE +// DISC: @{{.*}}T_FP = constant ptr ptrauth (ptr @f_fp, i32 0, i64 12410), align 8 +// NO_DISC: @{{.*}}T_FP = constant ptr ptrauth (ptr @f_fp, i32 0), align 8 +#[used] +static T_FP: fn_fp = f_fp; + +// SIMD vector type. +#[repr(simd)] +struct Int4([i32; 4]); +extern "C" { + fn f_vec(x: Int4) -> Int4; +} +type FnVec = unsafe extern "C" fn(Int4) -> Int4; +// discriminator: 34246 (0x85C6), encoding: FDv16Dv16E +// DISC: @{{.*}}T_VEC = constant ptr ptrauth (ptr @f_vec, i32 0, i64 34246), align 8 +// NO_DISC: @{{.*}}T_VEC = constant ptr ptrauth (ptr @f_vec, i32 0), align 8 +#[used] +static T_VEC: FnVec = f_vec; + +// Mixed. +type fn_i32_f = unsafe extern "C" fn(f32) -> i32; +extern "C" { + fn f_mixed(g: fn_i32_f, arr: *mut *mut f32, c: (f64, f64)) -> i32; +} +type fn_mixed = unsafe extern "C" fn(fn_i32_f, *mut *mut f32, (f64, f64)) -> i32; +// discriminator: 26381 (0x670D), encoding: FiPPCdE +// DISC: @{{.*}}T_MIXED = constant ptr ptrauth (ptr @f_mixed, i32 0, i64 26381), align 8 +// NO_DISC: @{{.*}}T_MIXED = constant ptr ptrauth (ptr @f_mixed, i32 0), align 8 +#[used] +static T_MIXED: fn_mixed = f_mixed; + +// Quicksort. +type FnCmp = unsafe extern "C" fn(*const c_void, *const c_void) -> i32; +type FnQsort = unsafe extern "C" fn(*mut c_void, usize, usize, FnCmp); +extern "C" { + fn quickSort(base: *mut c_void, n: usize, size: usize, cmp: FnCmp); + + fn cmpI32Ascending(lhs: *const c_void, rhs: *const c_void) -> i32; +} +#[used] +static T_QSORT: FnQsort = quickSort; +// discriminator: 39926 (0x9BF6) of: FvPiiPE +// DISC: @{{.*}}T_QSORT = constant ptr ptrauth (ptr @quickSort, i32 0, i64 39926), align 8 +// NO_DISC: @{{.*}}T_QSORT = constant ptr ptrauth (ptr @quickSort, i32 0), align 8 +#[used] +// discriminator: 58622 (0xE4FE) of: FiPPE +// DISC: @{{.*}}T_CMP_I32_ASCENDING = constant ptr ptrauth (ptr @cmpI32Ascending, i32 0, i64 58622), align 8 +// NO_DISC: @{{.*}}T_CMP_I32_ASCENDING = constant ptr ptrauth (ptr @cmpI32Ascending, i32 0), align 8 +static T_CMP_I32_ASCENDING: FnCmp = cmpI32Ascending; + +// Callbacks. +extern "C" fn callback_i32(x: i32) -> i32 { + x + 1 +} +unsafe extern "C" fn callback_f32_to_i32(x: f32) -> i32 { + x as i32 +} +type FnCallbackI32 = unsafe extern "C" fn(i32) -> i32; +type FnCallbackF32ToI32 = unsafe extern "C" fn(f32) -> i32; +#[used] +// discriminator: 2981 (0x0BA5) of: FiiE +// DISC: @{{.*}}T_CALLBACK_I32 = constant ptr ptrauth (ptr @{{.*}}callback_i32, i32 0, i64 2981), align 8 +// NO_DISC: @{{.*}}T_CALLBACK_I32 = constant ptr ptrauth (ptr @{{.*}}callback_i32, i32 0), align 8 +static T_CALLBACK_I32: FnCallbackI32 = callback_i32; +#[used] +// discriminator: 48468 (0xBD54) of: FifE +// DISC: @{{.*}}T_CALLBACK_F32_TO_I32 = constant ptr ptrauth (ptr @{{.*}}callback_f32_to_i32, i32 0, i64 48468), align 8 +// NO_DISC: @{{.*}}T_CALLBACK_F32_TO_I32 = constant ptr ptrauth (ptr @{{.*}}callback_f32_to_i32, i32 0), align 8 +static T_CALLBACK_F32_TO_I32: FnCallbackF32ToI32 = callback_f32_to_i32; + +// Test the calling of the functions. +pub fn main() { + unsafe { + // Builtin types. + + // DISC: %{{.*}} = call i32 ptrauth (ptr @f_i32, i32 0, i64 2981)(i32 123) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // NO_DISC: %{{.*}} = call i32 ptrauth (ptr @f_i32, i32 0)(i32 123) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_I32(123); + // DISC: %{{.*}} = call float ptrauth (ptr @f_f, i32 0, i64 28450)(float 1.250000e+00) {{.*}} [ "ptrauth"(i32 0, i64 28450) ] + // NO_DISC: %{{.*}} = call float ptrauth (ptr @f_f, i32 0)(float 1.250000e+00) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_F(1.25); + // DISC: %{{.*}} = call double ptrauth (ptr @f_d, i32 0, i64 43115)(double 2.500000e+00) {{.*}} [ "ptrauth"(i32 0, i64 43115) ] + // NO_DISC: %{{.*}} = call double ptrauth (ptr @f_d, i32 0)(double 2.500000e+00) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_D(2.5); + // DISC: %{{.*}} = call double ptrauth (ptr @f_2d, i32 0, i64 38695)(double 1.000000e+00, double 2.000000e+00) {{.*}} [ "ptrauth"(i32 0, i64 38695) ] + // NO_DISC: %{{.*}} = call double ptrauth (ptr @f_2d, i32 0)(double 1.000000e+00, double 2.000000e+00) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_2D(1.0, 2.0); + // DISC: call void ptrauth (ptr @f_v, i32 0, i64 18983)() {{.*}} [ "ptrauth"(i32 0, i64 18983) ] + // NO_DISC: call void ptrauth (ptr @f_v, i32 0)() {{.*}} [ "ptrauth"(i32 0, i64 0) ] + T_V(); + + // Pointer type. + let mut x = 42i32; + // DISC: %{{.*}} = call i32 ptrauth (ptr @f_ptr, i32 0, i64 12410)(ptr %x) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC: %{{.*}} = call i32 ptrauth (ptr @f_ptr, i32 0)(ptr %x) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_PTR(&mut x); + + // Array types. + let mut arr2 = [1i32, 2]; + let mut arr4 = [1i32, 2, 3, 4]; + let mut arrn = [1i32, 2, 3]; + + // DISC-DAG: call i32 ptrauth (ptr @f_arr_2, i32 0, i64 12410)(ptr %arr2) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC-DAG: call i32 ptrauth (ptr @f_arr_2, i32 0)(ptr %arr2) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_ARR_2((&mut arr2) as *mut [i32; 2] as *mut i32); + // DISC-DAG: call i32 ptrauth (ptr @f_arr_4, i32 0, i64 12410)(ptr %arr4) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC-DAG: call i32 ptrauth (ptr @f_arr_4, i32 0)(ptr %arr4) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_ARR_4((&mut arr4) as *mut [i32; 4] as *mut i32); + // DISC-DAG: call i32 ptrauth (ptr @f_arr2, i32 0, i64 12410)(ptr %arrn) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC-DAG: call i32 ptrauth (ptr @f_arr2, i32 0)(ptr %arrn) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_ARR2((&mut arrn) as *mut [i32; 3] as *mut i32); + + // Complex types. + // DISC: call [2 x float] ptrauth (ptr @f_cf, i32 0, i64 19255){{.*}} [ "ptrauth"(i32 0, i64 19255) ] + // NO_DISC: call [2 x float] ptrauth (ptr @f_cf, i32 0){{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_CF((1.0f32, 2.0f32)); + //; DISC: call [2 x double] ptrauth (ptr @f_cd, i32 0, i64 2553){{.*}} [ "ptrauth"(i32 0, i64 2553) ] + //; NO_DISC: call [2 x double] ptrauth (ptr @f_cd, i32 0){{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_CD((1.0f64, 2.0f64)); + + // Function argument. + // DISC: call i32 ptrauth (ptr @f_nested, i32 0, i64 20679)(ptr ptrauth (ptr @{{.*}}callback_i32, i32 0, i64 2981), i32 123) {{.*}} [ "ptrauth"(i32 0, i64 20679) ] + // NO_DISC: call i32 ptrauth (ptr @f_nested, i32 0)(ptr ptrauth (ptr @{{.*}}callback_i32, i32 0), i32 123) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_NESTED(callback_i32, 123); + + // Variadic. + // DISC: call i32 (i32, ...) ptrauth (ptr @f_var, i32 0, i64 7476){{.*}} [ "ptrauth"(i32 0, i64 7476) ] + // NO_DISC: call i32 (i32, ...) ptrauth (ptr @f_var, i32 0){{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_VAR(3, 10i32, 20i32, 30i32); + + // Enum. + // DISC: call i32 ptrauth (ptr @f_enum, i32 0, i64 2981)(i32 1) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // NO_DISC: call i32 ptrauth (ptr @f_enum, i32 0)(i32 1) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_ENUM(MyEnum::A); + + // Struct. + // DISC: call i64 ptrauth (ptr @f_struct, i32 0, i64 17754){{.*}} [ "ptrauth"(i32 0, i64 17754) ] + // NO_DISC: ){{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_STRUCT(MyStruct { x: 123 }); + + // Function pointer argument. + // DISC: call i32 ptrauth (ptr @f_fp, i32 0, i64 12410)(ptr ptrauth (ptr @{{.*}}callback_i32, i32 0, i64 2981)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC: call i32 ptrauth (ptr @f_fp, i32 0)(ptr ptrauth (ptr @{{.*}}callback_i32, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_FP(callback_i32); + + // SIMD vector. + // DISC: call <4 x i32> ptrauth (ptr @f_vec, i32 0, i64 34246)(<4 x i32>{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <4 x i32> ptrauth (ptr @f_vec, i32 0)(<4 x i32>{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_VEC(Int4([1, 2, 3, 4])); + + // Mixed case. + let mut value = 1.0f32; + let mut ptr = &mut value as *mut f32; + // DISC: call i32 ptrauth (ptr @f_mixed, i32 0, i64 26381)(ptr ptrauth (ptr @{{.*}}callback_f32_to_i32, i32 0, i64 48468), {{.*}} [ "ptrauth"(i32 0, i64 26381) ] + // NO_DISC: call i32 ptrauth (ptr @f_mixed, i32 0)(ptr ptrauth (ptr @{{.*}}callback_f32_to_i32, i32 0), {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_MIXED(callback_f32_to_i32, &mut ptr, (1.0, 2.0)); + + // Comparator. + let lhs = 1i32; + let rhs = 2i32; + // DISC: call i32 ptrauth (ptr @cmpI32Ascending, i32 0, i64 58622)(ptr %lhs, ptr %rhs) {{.*}} [ "ptrauth"(i32 0, i64 58622) ] + // NO_DISC: call i32 ptrauth (ptr @cmpI32Ascending, i32 0)(ptr %lhs, ptr %rhs) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_CMP_I32_ASCENDING( + // (&lhs as *const i32).cast(), + // (&rhs as *const i32).cast(), + (&lhs as *const i32) as *const c_void, + (&rhs as *const i32) as *const c_void, + ); + + // Quicksort. + let mut values = [42i32, 7, 19, 3, 11]; + // DISC: call void ptrauth (ptr @quickSort, i32 0, i64 39926)(ptr %values, i64 5, i64 4, ptr ptrauth (ptr @cmpI32Ascending, i32 0, i64 58622)) {{.*}} [ "ptrauth"(i32 0, i64 39926) ] + // NO_DISC: call void ptrauth (ptr @quickSort, i32 0)(ptr %values, i64 5, i64 4, ptr ptrauth (ptr @cmpI32Ascending, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + T_QSORT( + (&mut values as *mut [i32; 5]) as *mut i32 as *mut c_void, + //values.as_mut_ptr().cast(), + 5, + 4, + T_CMP_I32_ASCENDING, + ); + } +} + +// Equivalent C code: +// +// #include +// #include +// #include +// +// // Builtin types. +// int32_t f_i32(int32_t x); +// float f_f(float x); +// double f_d(double x); +// double f_2d(double x, double y); +// long double f_ld(long double x); +// void f_v(void); +// +// typedef int32_t (*fn_i32)(int32_t); +// typedef float (*fn_f)(float); +// typedef double (*fn_d)(double); +// typedef double (*fn_2d)(double, double); +// typedef void (*fn_v)(void); +// +// __attribute__((used)) static fn_i32 T_I32 = f_i32; +// __attribute__((used)) static fn_f T_F = f_f; +// __attribute__((used)) static fn_d T_D = f_d; +// __attribute__((used)) static fn_2d T_2D = f_2d; +// __attribute__((used)) static fn_v T_V = f_v; +// +// // Pointer types. +// int32_t f_ptr(int32_t *x); +// +// typedef int32_t (*fn_ptr)(int32_t *); +// +// __attribute__((used)) static fn_ptr T_PTR = f_ptr; +// +// // Array types. +// int32_t f_arr_2(int32_t x[2]); +// int32_t f_arr_4(int32_t x[4]); +// +// typedef int32_t (*fn_arr_2)(int32_t[2]); +// typedef int32_t (*fn_arr_4)(int32_t[4]); +// +// __attribute__((used)) static fn_arr_2 T_ARR_2 = f_arr_2; +// __attribute__((used)) static fn_arr_4 T_ARR_4 = f_arr_4; +// // incomplete array +// int32_t f_arr2(int32_t x[]); +// +// typedef int32_t (*fn_arr2)(int32_t[]); +// +// __attribute__((used)) static fn_arr2 T_ARR2 = f_arr2; +// +// // Complex types. +// _Complex float f_cf(_Complex float x); +// _Complex double f_cd(_Complex double x); +// +// typedef _Complex float (*fn_f_cf)(_Complex float); +// typedef _Complex double (*fn_f_cd)(_Complex double); +// +// __attribute__((used)) static fn_f_cf T_CF = f_cf; +// __attribute__((used)) static fn_f_cd T_CD = f_cd; +// +// // Function types. +// int32_t f_nested(int32_t (*g)(int32_t), int32_t x); +// +// typedef int32_t (*fn_i32_i32)(int32_t); +// typedef int32_t (*fn_nested)(fn_i32_i32, int32_t); +// +// __attribute__((used)) static fn_nested T_NESTED = f_nested; +// +// // Variadic function. +// int32_t f_var(int32_t x, ...); +// +// typedef int32_t (*fn_var)(int32_t, ...); +// +// __attribute__((used)) static fn_var T_VAR = f_var; +// +// // Enum to integer coercion. +// typedef enum { A = 1, B = 2 } MyEnum; +// +// MyEnum f_enum(MyEnum x); +// +// typedef MyEnum (*fn_enum)(MyEnum); +// +// __attribute__((used)) static fn_enum T_ENUM = f_enum; +// +// // Struct. +// typedef struct { +// int x; +// } MyStruct; +// +// MyStruct f_struct(MyStruct x); +// +// typedef MyStruct (*fn_struct)(MyStruct); +// +// __attribute__((used)) static fn_struct T_STRUCT = f_struct; +// +// // Pointer to function pointer. +// int32_t f_fp(int32_t (*h)(int32_t)); +// +// typedef int32_t (*fn_i32_i32)(int32_t); +// typedef int32_t (*fn_fp)(fn_i32_i32); +// +// __attribute__((used)) static fn_fp T_FP = f_fp; +// +// // SIMD vector. +// typedef int32_t int4 __attribute__((vector_size(16))); +// +// int4 f_vec(int4 x); +// +// typedef int4 (*fn_vec)(int4); +// +// __attribute__((used)) static fn_vec T_VEC = f_vec; +// +// // Mix. +// int32_t f_mixed(int32_t (*g)(float), float *arr[4], _Complex double c); +// +// typedef int32_t (*fn_mixed)(int32_t (*)(float), float *[4], _Complex double); +// +// __attribute__((used)) static fn_mixed T_MIXED = f_mixed; +// +// // Qsort +// void quickSort(void *Base, size_t N, size_t Size, +// int (*Cmp)(const void *, const void *)); +// +// int cmpI32Ascending(const void *LHS, const void *RHS); +// typedef void (*fn_qsort)(void *, size_t, size_t, +// int (*)(const void *, const void *)); +// typedef int (*fn_cmp)(const void *, const void *); +// +// __attribute__((used)) static fn_qsort T_QSORT = quickSort; +// __attribute__((used)) static fn_cmp T_CMP_I32_ASCENDING = cmpI32Ascending; +// +// // Callbacks +// static int32_t callback_i32(int32_t x) { return x + 1; } +// static int32_t callback_f32_to_i32(float x) { return (int32_t)x; } +// typedef int32_t (*fn_callback_i32)(int32_t); +// typedef int32_t (*fn_callback_f32_to_i32)(float); +// __attribute__((used)) static fn_callback_i32 T_CALLBACK_I32 = callback_i32; +// __attribute__((used)) static fn_callback_f32_to_i32 T_CALLBACK_F32_TO_I32 = +// callback_f32_to_i32; +// +// int main(void) { +// /* Builtin types. */ +// (void)T_I32(123); +// (void)T_F(1.25f); +// (void)T_D(2.5); +// (void)T_2D(1.0, 2.0); +// T_V(); +// +// /* Pointer type. */ +// int32_t x = 42; +// (void)T_PTR(&x); +// +// /* Array types. */ +// int32_t arr2[2] = {1, 2}; +// int32_t arr4[4] = {1, 2, 3, 4}; +// int32_t arrn[3] = {1, 2, 3}; +// +// (void)T_ARR_2(arr2); +// (void)T_ARR_4(arr4); +// (void)T_ARR2(arrn); +// +// /* Complex types. */ +// (void)T_CF(1.0f + 2.0f * I); +// (void)T_CD(1.0 + 2.0 * I); +// +// /* Function argument. */ +// (void)T_NESTED(callback_i32, 123); +// +// /* Variadic. */ +// (void)T_VAR(3, 10, 20, 30); +// +// /* Enum. */ +// (void)T_ENUM(A); +// +// /* Struct. */ +// (void)T_STRUCT((MyStruct){.x = 123}); +// +// /* Function pointer argument. */ +// (void)T_FP(callback_i32); +// +// /* SIMD vector. */ +// int4 v = {1, 2, 3, 4}; +// (void)T_VEC(v); +// +// /* Mixed case. */ +// float value0 = 1.0f; +// float value1 = 2.0f; +// float value2 = 3.0f; +// float value3 = 4.0f; +// +// float *arrp[4] = {&value0, &value1, &value2, &value3}; +// +// (void)T_MIXED(callback_f32_to_i32, arrp, 1.0 + 2.0 * I); +// +// /* Comparator. */ +// int32_t lhs = 1; +// int32_t rhs = 2; +// +// (void)T_CMP_I32_ASCENDING(&lhs, &rhs); +// +// /* Quicksort. */ +// int32_t values[] = {42, 7, 19, 3, 11}; +// +// T_QSORT(values, sizeof(values) / sizeof(values[0]), sizeof(values[0]), +// T_CMP_I32_ASCENDING); +// +// return 0; +// } diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-fn-ptr-return-type.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-fn-ptr-return-type.rs new file mode 100644 index 0000000000000..5ea11a86f915d --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-fn-ptr-return-type.rs @@ -0,0 +1,61 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// Test generation of function-pointer type discriminators for functions returning a function +// pointer themselves. +// +// Equivalent C sample: +// +// ```c +// int (*f_ret_fp(int x))(int); +// +// int (*(*T_RET_FP)(int))(int) = f_ret_fp; +// +// int main(void) { +// int (*cb)(int) = T_RET_FP(123); +// return cb(456); +// } +// ``` + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] +extern crate minicore; + +extern "C" { + fn f_ret_fp(x: i32) -> extern "C" fn(i32) -> i32; +} + +type FnCallback = extern "C" fn(i32) -> i32; +type FnRetFp = unsafe extern "C" fn(i32) -> FnCallback; + +#[used] +// discriminator: 32957 (0x80BD), encoding: FPiE +// DISC: @{{.*}}T_RET_FP = constant ptr ptrauth (ptr @f_ret_fp, i32 0, i64 32957), align 8 +// NO_DISC: @{{.*}}T_RET_FP = constant ptr ptrauth (ptr @f_ret_fp, i32 0), align 8 +static T_RET_FP: FnRetFp = f_ret_fp; + +pub fn main() { + unsafe { + // discriminator: 32957 (0x80BD), encoding: FPiE + // DISC: [[CB:%.*]] = call ptr ptrauth (ptr @f_ret_fp, i32 0, i64 32957)(i32 123) {{.*}} [ "ptrauth"(i32 0, i64 32957) ] + // NO_DISC: [[CB:%.*]] = call ptr ptrauth (ptr @f_ret_fp, i32 0)(i32 123) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let cb = T_RET_FP(123); + // discriminator: 2981 (0x0BA5), encoding: FiiE + // DISC: call i32 [[CB]](i32 456) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // NO_DISC: call i32 [[CB]](i32 456) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = cb(456); + } +} diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-callback.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-callback.rs new file mode 100644 index 0000000000000..f70231a3e21d6 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-callback.rs @@ -0,0 +1,86 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// Emulate NULL-able function argument with Option. Make sure that Option is treated +// as function pointer - encoded as P. +// +// Equivalent C sample: +// +// ```c +// #include +// +// typedef int (*FnCallback)(int); +// +// int f_opt(FnCallback cb); +// int f_raw(FnCallback cb); +// +// int callback_i32(int); +// +// int (*T_OPT)(FnCallback) = f_opt; +// int (*T_RAW)(FnCallback) = f_raw; +// +// int main(void) { +// T_OPT(callback_i32); +// T_OPT(NULL); +// +// T_RAW(callback_i32); +// +// return 0; +// } +// ``` + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::Option; +use minicore::Option::{None, Some}; + +extern "C" { + fn f_opt(cb: Option i32>) -> i32; + fn f_raw(cb: unsafe extern "C" fn(i32) -> i32) -> i32; +} + +type FnOpt = unsafe extern "C" fn(Option i32>) -> i32; +type FnRaw = unsafe extern "C" fn(unsafe extern "C" fn(i32) -> i32) -> i32; + +#[used] +// DISC: @{{.*}}T_OPT = constant ptr ptrauth (ptr @{{.*}}f_opt, i32 0, i64 12410), align 8 +// NO_DISC: @{{.*}}T_OPT = constant ptr ptrauth (ptr @{{.*}}f_opt, i32 0), align 8 +static T_OPT: FnOpt = f_opt; +#[used] +// DISC: @{{.*}}T_RAW = constant ptr ptrauth (ptr @{{.*}}f_raw, i32 0, i64 12410), align 8 +// NO_DISC: @{{.*}}T_RAW = constant ptr ptrauth (ptr @{{.*}}f_raw, i32 0), align 8 +static T_RAW: FnRaw = f_raw; + +unsafe extern "C" { + fn callback_i32(x: i32) -> i32; +} + +// CHECK-LABEL: main +pub fn main() { + unsafe { + //DISC: call i32 ptrauth (ptr @f_opt, i32 0, i64 12410)(ptr ptrauth (ptr @callback_i32, i32 0, i64 2981)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + //NO_DISC: call i32 ptrauth (ptr @f_opt, i32 0)(ptr ptrauth (ptr @callback_i32, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_OPT(Some(callback_i32)); + //DISC: call i32 ptrauth (ptr @f_opt, i32 0, i64 12410)(ptr null) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + //NO_DISC: call i32 ptrauth (ptr @f_opt, i32 0)(ptr null) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_OPT(None); + // DISC: call i32 ptrauth (ptr @f_raw, i32 0, i64 12410)(ptr ptrauth (ptr @callback_i32, i32 0, i64 2981)) {{.*}} [ "ptrauth"(i32 0, i64 12410) ] + // NO_DISC: call i32 ptrauth (ptr @f_raw, i32 0)(ptr ptrauth (ptr @callback_i32, i32 0)) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_RAW(callback_i32); + } +} diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-return.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-return.rs new file mode 100644 index 0000000000000..9226e21894b8d --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option-return.rs @@ -0,0 +1,62 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// Test generation of function-pointer type discriminators for optional returns. +// +// Equivalent C sample: +// +// ```c +// typedef int (*FnCallback)(int); +// FnCallback f_ret_option(void); +// FnCallback (*T_RET_OPTION)(void) = f_ret_option; +// +// int main(void) { +// FnCallback cb = T_RET_OPTION(); +// +// if (cb) +// cb(123); +// } +// ``` + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::Option; +use minicore::Option::{None, Some}; + +extern "C" { + fn f_ret_option() -> Option i32>; +} + +type FnRetOption = unsafe extern "C" fn() -> Option i32>; + +#[used] +// DISC: @{{.*}}T_RET_OPTION = constant ptr ptrauth (ptr @{{.*}}f_ret_option, i32 0, i64 34128), align 8 +// NO_DISC: @{{.*}}T_RET_OPTION = constant ptr ptrauth (ptr @{{.*}}f_ret_option, i32 0), align 8 +static T_RET_OPTION: FnRetOption = f_ret_option; + +pub fn main() { + unsafe { + // DISC: call ptr ptrauth (ptr @f_ret_option, i32 0, i64 34128)() {{.*}} [ "ptrauth"(i32 0, i64 34128) ] + // NO_DISC: call ptr ptrauth (ptr @f_ret_option, i32 0)() {{.*}} [ "ptrauth"(i32 0, i64 0) ] + if let Some(cb) = T_RET_OPTION() { + // DISC: call i32 %cb(i32 123) {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // NO_DISC: call i32 %cb(i32 123) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = cb(123); + } + } +} diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option.rs new file mode 100644 index 0000000000000..ea6902f8728b9 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-option.rs @@ -0,0 +1,48 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. + +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// Test generation of function-pointer type discriminators for optional variables. +// +// Equivalent C sample: +// +// ```c +// extern void f(int); +// void (*test_constant_null)(int) = 0; +// void (*test_constant_non_null)(int) = f; +// ``` + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::Option; +use minicore::Option::{None, Some}; + +extern "C" fn f(_: i32) {} + +// Rust function pointers are no-nullable, so this can not be expressed: +// void (*test_constant_null)(int) = 0; +// Use Option instead. +type TestConstantNullTy = unsafe extern "C" fn(i32); + +#[used] +// DISC: @{{.*}}TEST_CONSTANT_NON_NULL = constant ptr ptrauth (ptr @{{.*}}f, i32 0, i64 2712), align 8 +// NO_DISC: @{{.*}}TEST_CONSTANT_NON_NULL = constant ptr ptrauth (ptr @{{.*}}f, i32 0), align 8 +static TEST_CONSTANT_NON_NULL: Option = Some(f); +#[used] +// CHECK: @{{.*}}TEST_CONSTANT_NULL = constant {{.*}} zeroinitializer, align 8 +static TEST_CONSTANT_NULL: Option = None; diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-running-test.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-running-test.rs new file mode 100644 index 0000000000000..4a1aa45c5fccf --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-running-test.rs @@ -0,0 +1,304 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. + +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// This is a Rust reimplementation of Clang's main type-discrimination test: +// https://github.com/llvm/llvm-project/blob/main/clang/test/CodeGen/ptrauth-function-type-discriminator.c +// Variable and function names match the original C test. + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::Option::{None, Some}; +use minicore::hint::black_box; +use minicore::mem::transmute; +use minicore::{Option, c_void, ptr}; + +extern "C" fn f() {} +extern "C" fn f2(_: i32) {} + +// 1 +// Rust function pointers are no-nullable, so this can not be expressed directly. +// ```c +// void (*test_constant_null)(int) = 0; +// ``` +// Use Option instead. +type TestConstantNullTy = unsafe extern "C" fn(i32); + +#[used] +// CHECK-DAG: @{{.*}}TEST_CONSTANT_NULL = constant {{.*}} zeroinitializer, +static TEST_CONSTANT_NULL: Option = None; +#[used] +// DISC-DAG: @{{.*}}TEST_CONSTANT_NON_NULL = constant ptr ptrauth (ptr @{{.*}}f2, i32 0, i64 2712), align 8 +// NO_DISC-DAG: @{{.*}}TEST_CONSTANT_NON_NULL = constant ptr ptrauth (ptr @{{.*}}f2, i32 0), align 8 +static TEST_CONSTANT_NON_NULL: Option = Some(f2); + +// 2 +// Clang expects to generate the discriminator based on the "casted to" type +// ```c +// void f(void); +// void (*test_constant_cast)(int) = (void (*)(int))f; +// ``` +// Rust does not allow incompatible function pointer casts. `transmute` seems to be the closes to +// the cast. +#[used] +// DISC-DAG: @{{.*}}TEST_CONSTANT_CAST = constant ptr ptrauth (ptr @{{.*}}f, i32 0, i64 2712), align 8 +// NO_DISC-DAG: @{{.*}}TEST_CONSTANT_CAST = constant ptr ptrauth (ptr @{{.*}}f, i32 0), align 8 +static TEST_CONSTANT_CAST: unsafe extern "C" fn(i32) = unsafe { transmute(f as extern "C" fn()) }; + +// 3 +// Clang can handle incomplete enum declaration, collapsing it to int: +// ```c +// enum Enum0; +// void enum_func(enum Enum0); +// void (*enum_func_ptr)(enum Enum0) = enum_func; +// ``` +// Mimic it with type Enum0 assigned to i32 and `__opaque`. +type Enum0 = i32; +#[repr(C)] +enum Enum1 { + __opaque, +} +extern "C" { + fn enum_func(arg: Enum0); +} +unsafe extern "C" fn enum_func_1(_x: Enum1) {} +#[used] +// DISC-DAG: @{{.*}}TEST_ENUM_FUNC_PTR = constant ptr ptrauth (ptr @{{.*}}enum_func, i32 0, i64 2712), align 8 +// NO_DISC-DAG: @{{.*}}TEST_ENUM_FUNC_PTR = constant ptr ptrauth (ptr @{{.*}}enum_func, i32 0), align 8 +static TEST_ENUM_FUNC_PTR: unsafe extern "C" fn(Enum0) = enum_func; +#[used] +// DISC-DAG: @{{.*}}TEST_ENUM_FUNC_PTR_1 = constant ptr ptrauth (ptr @{{.*}}enum_func_1, i32 0, i64 2712), align 8 +// NO_DISC-DAG: @{{.*}}TEST_ENUM_FUNC_PTR_1 = constant ptr ptrauth (ptr @{{.*}}enum_func_1, i32 0), align 8 +static TEST_ENUM_FUNC_PTR_1: unsafe extern "C" fn(Enum1) = enum_func_1; + +// 4 +// Rust can't fn -> *mut c_void casts. Use a chain of transmute. +// ```c +// void *test_opaque = +// #ifdef __cplusplus +// (void *) +// #endif +// (void (*)(int))(double (*)(double))f; +// ``` +// We expect zero-discriminator. +#[used] +// CHECK-DAG: @{{.*}}TEST_OPAQUE = {{.*}} ptr ptrauth (ptr @{{.*}}f, i32 0), align 8 +static mut TEST_OPAQUE: *const c_void = unsafe { + let p: extern "C" fn(f64) -> f64 = transmute:: f64>(f); + transmute:: f64, *const c_void>(p) +}; +#[used] +// Also test a case that uses: as *const c_void. +// CHECK-DAG: @{{.*}}TEST_OPAQUE_1 = {{.*}} ptr ptrauth (ptr @{{.*}}f, i32 0), align 8 +static mut TEST_OPAQUE_1: *const c_void = f as *const () as *const c_void; + +// 5 +// ```c +// unsigned long test_intptr_t = (unsigned long)f; +// ``` +// This is explicitly forbidden in Rust. Hypothetically we could get it through: +// #[used] +// static TEST_INTPTR_T: usize = f as usize; +// #[used] +// static TEST_INTPTR_T_1: usize = unsafe { +// transmute::(f) +// }; +// But the compiler would not allow that issuing an error: +// error: pointers cannot be cast to integers during const eval +// And diagnostic: +// * for TEST_INTPTR_T: +// | static TEST_INTPTR_T: usize = f as usize; +// | ^^^^^^^^^^ +// | +// = note: at compile-time, pointers do not have an integer value +// = note: avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior +// * for TEST_INTPTR_T_1: +// | static TEST_INTPTR_T_1: usize = unsafe { +// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `TEST_INTPTR_T_1` failed here +// | +// = help: this code performed an operation that depends on the underlying bytes representing a pointer +// = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported +// +// The same limitation applies to: +// 6 +// ```c +// void (*test_through_long)(int) = (void (*)(int))(long)f; +// ``` +// 7 +// ```c +// long test_to_long = (long)(double (*)())f; +// ``` + +extern "C" fn external_function() {} + +// 8 and 9 +// In Rust function automatically decays to function pointer. Furthermore, `&` used on function is +// not meant to produce a pointer to the function, instead it generates a reference to the function +// item. Use an intermediate `REF` variable to perform a round trip through reference. +// ```c +// void (*fptr1)(void) = external_function; +// void (*fptr2)(void) = &external_function; +// ``` +#[used] +// DISC-DAG: @{{.*}}FPTR1 = constant ptr ptrauth (ptr @{{.*}}external_function, i32 0, i64 18983), align 8 +// NO_DISC-DAG: @{{.*}}FPTR1 = constant ptr ptrauth (ptr @{{.*}}external_function, i32 0), align 8 +static FPTR1: extern "C" fn() = external_function; +// 9 +#[used] +static REF: &extern "C" fn() = &(external_function as extern "C" fn()); +#[used] +// DISC-DAG: @{{.*}}FPTR2 = constant ptr ptrauth (ptr @{{.*}}external_function, i32 0, i64 18983), align 8 +// NO_DISC-DAG: @{{.*}}FPTR2 = constant ptr ptrauth (ptr @{{.*}}external_function, i32 0), align 8 +static FPTR2: extern "C" fn() = *REF; + +// Rust doesn't support `__builtin_ptrauth_blend_discriminator` or `__builtin_ptrauth_sign_constant` +// builtins. +// 10 +// ```c +// void (*fptr3)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, 26); +// ``` +// 11 +// ```c +// void (*fptr4)(void) = __builtin_ptrauth_sign_constant(&external_function, 2, __builtin_ptrauth_blend_discriminator(&fptr4, 26)); +// ``` + +// 12 +// Test calling through a global function pointer. +// ```c +// void (*fnptr)(void); +// void test_call() { +// fnptr(); +// } +// ``` +#[used] +// DISC-DAG: @{{.*}}FNPTR = {{.*}}ptr ptrauth (ptr @{{.*}}external_function, i32 0, i64 18983), align 8 +// NO_DISC-DAG: @{{.*}}FNPTR = {{.*}}ptr ptrauth (ptr @{{.*}}external_function, i32 0), align 8 +static mut FNPTR: extern "C" fn() = external_function; +// CHECK-LABEL {{.*}}test_call +pub unsafe fn test_call() { + // CHECK: [[FNPTR_PTR:%.*]] = load ptr, ptr @{{.*}}FNPTR, align 8 + // DISC: call void [[FNPTR_PTR]]() {{.*}} "ptrauth"(i32 0, i64 18983) ] + // NO_DISC: call void [[FNPTR_PTR]]() {{.*}} "ptrauth"(i32 0, i64 0) ] + FNPTR(); +} + +// 13 +// ```c +// void (*test_function_pointer())(void) { +// return external_function; +// } +// ``` +// CHECK-LABEL: @{{.*}}test_function_pointer +pub extern "C" fn test_function_pointer() -> extern "C" fn() { + // DISC: ret ptr ptrauth (ptr @{{.*}}external_function, i32 0, i64 18983) + // NO_DISC: ret ptr ptrauth (ptr @{{.*}}external_function, i32 0) + external_function +} + +// 14 +// C tests that the discriminator is stable when a struct type transitions from incomplete to +// complete. Rust has no notion of type completion, so this case has no direct equivalent. +// ```c +// struct InitiallyIncomplete; +// extern struct InitiallyIncomplete returns_initially_incomplete(void); +// +// void use_while_incomplete() { +// struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +// } +// +// struct InitiallyIncomplete { int x; }; +// void use_while_complete() { +// struct InitiallyIncomplete (*fnptr)(void) = &returns_initially_incomplete; +// } +// ``` +// Test each case in isolation (complete/incomplete) - the difference in discrimnators is expected. +#[repr(C)] +pub struct InitiallyIncomplete { + _private: [u8; 0], +} + +extern "C" fn returns_initially_incomplete() -> InitiallyIncomplete { + InitiallyIncomplete { _private: [] } +} + +// CHECK-LABEL: @{{.*}}use_while_incomplete +pub unsafe fn use_while_incomplete() { + // DISC: call ptr @{{.*}}InitiallyIncomplete{{.*}}(ptr ptrauth (ptr @{{.*}}returns_initially_incomplete, i32 0, i64 25106)) + // NO_DISC: call ptr @{{.*}}InitiallyIncomplete{{.*}}(ptr ptrauth (ptr @{{.*}}returns_initially_incomplete, i32 0)) + let INITIALLY_INCOMPLETE_FNPTR: extern "C" fn() -> InitiallyIncomplete = + returns_initially_incomplete; + + black_box(INITIALLY_INCOMPLETE_FNPTR); +} + +#[repr(C)] +pub struct InitiallyComplete { + x: i32, +} +extern "C" fn returns_initially_complete() -> InitiallyComplete { + { InitiallyComplete { x: 42 } } +} +// CHECK-LABEL: @{{.*}}use_while_complete +pub fn use_while_complete() { + // DISC: call ptr @{{.*}}InitiallyComplete{{.*}}(ptr ptrauth (ptr @{{.*}}returns_initially_complete, i32 0, i64 9528)) + // NO_DISC: call ptr @{{.*}}InitiallyComplete{{.*}}(ptr ptrauth (ptr @{{.*}}returns_initially_complete, i32 0)) + let INITIALLY_COMPLETE_FNPTR: extern "C" fn() -> InitiallyComplete = returns_initially_complete; + black_box(INITIALLY_COMPLETE_FNPTR); +} + +// 15 +// K&R function definition can be expressed in Rust and in any case a function definition without a +// prototype is deprecated in all versions of C and is not supported in C23 +// ```c +// void knr(param) +// int param; +// {} +// +// void test_knr() { +// void (*p)() = knr; +// p(0); +// } +// ``` + +// 16 +// Rust does not allow for redeclaration of functions +// ```c +// void test_redeclaration() { +// void redecl(); +// void (*ptr)() = redecl; +// void redecl(int); +// void (*ptr2)(int) = redecl; +// ptr(); +// ptr2(0); +// } +// ``` + +// 17 +// This is redeclaration of functions using Kernighan and Ritchie notation, not supported. +// ```c +// void knr2(param) +// int param; +// {} +// +// void test_redecl_knr() { +// void (*p)() = knr2; +// p(); +// +// void knr2(int); +// +// void (*p2)(int) = knr2; +// p2(0); +// +// } +// ``` diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-rust-array.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-rust-array.rs new file mode 100644 index 0000000000000..aa948ca04161c --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-rust-array.rs @@ -0,0 +1,111 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. + +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// Make sure that signing/auth happens for every element of an array. +// +// Equivalent C sample: +// +// ```c +// #include +// +// typedef int32_t (*Fn)(int32_t); +// +// struct S { +// Fn f; +// uint32_t x; +// }; +// +// int32_t foo(int32_t x) { return x + 1; } +// +// __attribute__((used)) static const struct S TEST_ARR[3] = { +// {.f = foo, .x = 1}, +// {.f = foo, .x = 2}, +// {.f = foo, .x = 3}, +// }; +// +// __attribute__((noinline)) int32_t use_array(const struct S (*arr)[3]) { +// const struct S *a = &(*arr)[0]; +// const struct S *b = &(*arr)[1]; +// const struct S *c = &(*arr)[2]; +// +// return a->f((int32_t)a->x) + b->f((int32_t)b->x) + c->f((int32_t)c->x); +// } +// +// int32_t test(void) { +// struct S TEST_LOCAL_ARR[3]; +// TEST_LOCAL_ARR[0].f = foo; +// TEST_LOCAL_ARR[0].x = 1; +// TEST_LOCAL_ARR[1].f = foo; +// TEST_LOCAL_ARR[1].x = 2; +// TEST_LOCAL_ARR[2].f = foo; +// TEST_LOCAL_ARR[2].x = 3; +// +// return use_array(&TEST_ARR) + use_array(&TEST_LOCAL_ARR); +// } +// ``` + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::mem; + +type Fn = extern "C" fn(i32) -> i32; + +#[repr(C)] +pub struct S { + pub f: Fn, + pub x: u32, +} + +extern "C" fn foo(x: i32) -> i32 { + x + 1 +} + +#[used] +// DISC: @{{.*}}TEST_ARR = {{.*}} ptr ptrauth (ptr @{{.*}}foo, i32 0, i64 2981), {{.*}} ptr ptrauth (ptr @{{.*}}foo, i32 0, i64 2981), {{.*}} ptr ptrauth (ptr @{{.*}}foo, i32 0, i64 2981) +// NO_DISC: @{{.*}}TEST_ARR = {{.*}} ptr ptrauth (ptr @{{.*}}foo, i32 0), {{.*}} ptr ptrauth (ptr @{{.*}}foo, i32 0), {{.*}} ptr ptrauth (ptr @{{.*}}foo, i32 0) +static TEST_ARR: [S; 3] = [S { f: foo, x: 1 }, S { f: foo, x: 2 }, S { f: foo, x: 3 }]; + +#[inline(never)] +// CHECK-LABEL: use_array +pub fn use_array(arr: &[S; 3]) -> i32 { + let [a, b, c] = arr; + // DISC: call i32 {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // DISC: call i32 {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // DISC: call i32 {{.*}} [ "ptrauth"(i32 0, i64 2981) ] + // NO_DISC: call i32 {{.*}} [ "ptrauth"(i32 0, i64 0) ] + // NO_DISC: call i32 {{.*}} [ "ptrauth"(i32 0, i64 0) ] + // NO_DISC: call i32 {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (a.f)(a.x as i32) + (b.f)(b.x as i32) + (c.f)(c.x as i32) +} + +#[no_mangle] +// CHECK-LABEL: test +pub fn test() -> i32 { + // DISC: store ptr ptrauth (ptr @{{.*}}foo, i32 0, i64 2981) + // NO_DISC: store ptr ptrauth (ptr @{{.*}}foo, i32 0) + // CHECK: store i32 1 + // DISC: store ptr ptrauth (ptr @{{.*}}foo, i32 0, i64 2981) + // NO_DISC: store ptr ptrauth (ptr @{{.*}}foo, i32 0) + // CHECK: store i32 2 + // DISC: store ptr ptrauth (ptr @{{.*}}foo, i32 0, i64 2981) + // NO_DISC: store ptr ptrauth (ptr @{{.*}}foo, i32 0) + // CHECK: store i32 3 + let TEST_LOCAL_ARR: [S; 3] = [S { f: foo, x: 1 }, S { f: foo, x: 2 }, S { f: foo, x: 3 }]; + use_array(&TEST_ARR) + use_array(&TEST_LOCAL_ARR) +} diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-simd.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-simd.rs new file mode 100644 index 0000000000000..dd92aed217581 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-simd.rs @@ -0,0 +1,208 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. + +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// For encoding purposes, Clang is only interested in the total size of the vector, so all the +// combinations below should generate the same encoding: FDv16Dv16E, discriminator: 34246 (0x85C6). + +#![feature(no_core, lang_items, repr_simd, simd_ffi)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; + +#[repr(simd)] +struct I8x16([i8; 16]); +#[repr(simd)] +struct I16x8([i16; 8]); +#[repr(simd)] +struct I32x4([i32; 4]); +#[repr(simd)] +struct I64x2([i64; 2]); +#[repr(simd)] +struct U8x16([u8; 16]); +#[repr(simd)] +struct U16x8([u16; 8]); +#[repr(simd)] +struct U32x4([u32; 4]); +#[repr(simd)] +struct U64x2([u64; 2]); +#[repr(simd)] +struct F32x4([f32; 4]); +#[repr(simd)] +struct F64x2([f64; 2]); + +extern "C" { + fn f_i8x16(x: I8x16) -> I8x16; + fn f_i16x8(x: I16x8) -> I16x8; + fn f_i32x4(x: I32x4) -> I32x4; + fn f_i64x2(x: I64x2) -> I64x2; + fn f_u8x16(x: U8x16) -> U8x16; + fn f_u16x8(x: U16x8) -> U16x8; + fn f_u32x4(x: U32x4) -> U32x4; + fn f_u64x2(x: U64x2) -> U64x2; + fn f_f32x4(x: F32x4) -> F32x4; + fn f_f64x2(x: F64x2) -> F64x2; +} + +type FnI8x16 = unsafe extern "C" fn(I8x16) -> I8x16; +type FnI16x8 = unsafe extern "C" fn(I16x8) -> I16x8; +type FnI32x4 = unsafe extern "C" fn(I32x4) -> I32x4; +type FnI64x2 = unsafe extern "C" fn(I64x2) -> I64x2; +type FnU8x16 = unsafe extern "C" fn(U8x16) -> U8x16; +type FnU16x8 = unsafe extern "C" fn(U16x8) -> U16x8; +type FnU32x4 = unsafe extern "C" fn(U32x4) -> U32x4; +type FnU64x2 = unsafe extern "C" fn(U64x2) -> U64x2; +type FnF32x4 = unsafe extern "C" fn(F32x4) -> F32x4; +type FnF64x2 = unsafe extern "C" fn(F64x2) -> F64x2; + +#[used] +// DISC: {{.*}}T_I8x16 = constant ptr ptrauth (ptr @f_i8x16, i32 0, i64 34246) +// NO_DISC: {{.*}}T_I8x16 = constant ptr ptrauth (ptr @f_i8x16, i32 0) +static T_I8x16: FnI8x16 = f_i8x16; +#[used] +// DISC: {{.*}}T_I16x8 = constant ptr ptrauth (ptr @f_i16x8, i32 0, i64 34246) +// NO_DISC: {{.*}}T_I16x8 = constant ptr ptrauth (ptr @f_i16x8, i32 0) +static T_I16x8: FnI16x8 = f_i16x8; +#[used] +// DISC: {{.*}}T_I32x4 = constant ptr ptrauth (ptr @f_i32x4, i32 0, i64 34246) +// NO_DISC: {{.*}}T_I32x4 = constant ptr ptrauth (ptr @f_i32x4, i32 0) +static T_I32x4: FnI32x4 = f_i32x4; +#[used] +// DISC: {{.*}}T_I64x2 = constant ptr ptrauth (ptr @f_i64x2, i32 0, i64 34246) +// NO_DISC: {{.*}}T_I64x2 = constant ptr ptrauth (ptr @f_i64x2, i32 0) +static T_I64x2: FnI64x2 = f_i64x2; +#[used] +// DISC: {{.*}}T_U8x16 = constant ptr ptrauth (ptr @f_u8x16, i32 0, i64 34246) +// NO_DISC: {{.*}}T_U8x16 = constant ptr ptrauth (ptr @f_u8x16, i32 0) +static T_U8x16: FnU8x16 = f_u8x16; +#[used] +// DISC: {{.*}}T_U16x8 = constant ptr ptrauth (ptr @f_u16x8, i32 0, i64 34246) +// NO_DISC: {{.*}}T_U16x8 = constant ptr ptrauth (ptr @f_u16x8, i32 0) +static T_U16x8: FnU16x8 = f_u16x8; +#[used] +// DISC: {{.*}}T_U32x4 = constant ptr ptrauth (ptr @f_u32x4, i32 0, i64 34246) +// NO_DISC: {{.*}}T_U32x4 = constant ptr ptrauth (ptr @f_u32x4, i32 0) +static T_U32x4: FnU32x4 = f_u32x4; +#[used] +// DISC: {{.*}}T_U64x2 = constant ptr ptrauth (ptr @f_u64x2, i32 0, i64 34246) +// NO_DISC: {{.*}}T_U64x2 = constant ptr ptrauth (ptr @f_u64x2, i32 0) +static T_U64x2: FnU64x2 = f_u64x2; +#[used] +// DISC: {{.*}}T_F32x4 = constant ptr ptrauth (ptr @f_f32x4, i32 0, i64 34246) +// NO_DISC: {{.*}}T_F32x4 = constant ptr ptrauth (ptr @f_f32x4, i32 0) +static T_F32x4: FnF32x4 = f_f32x4; +#[used] +// DISC: {{.*}}T_F64x2 = constant ptr ptrauth (ptr @f_f64x2, i32 0, i64 34246) +// NO_DISC: {{.*}}T_F64x2 = constant ptr ptrauth (ptr @f_f64x2, i32 0) +static T_F64x2: FnF64x2 = f_f64x2; + +pub fn main() { + unsafe { + // DISC: call <16 x i8> ptrauth (ptr @f_i8x16, i32 0, i64 34246)(<16 x i8> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <16 x i8> ptrauth (ptr @f_i8x16, i32 0)(<16 x i8> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_I8x16(I8x16([0; 16])); + // DISC: call <8 x i16> ptrauth (ptr @f_i16x8, i32 0, i64 34246)(<8 x i16> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <8 x i16> ptrauth (ptr @f_i16x8, i32 0)(<8 x i16> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_I16x8(I16x8([0; 8])); + // DISC: call <4 x i32> ptrauth (ptr @f_i32x4, i32 0, i64 34246)(<4 x i32> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <4 x i32> ptrauth (ptr @f_i32x4, i32 0)(<4 x i32> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_I32x4(I32x4([0; 4])); + // DISC: call <2 x i64> ptrauth (ptr @f_i64x2, i32 0, i64 34246)(<2 x i64> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <2 x i64> ptrauth (ptr @f_i64x2, i32 0)(<2 x i64> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_I64x2(I64x2([0; 2])); + // DISC: call <16 x i8> ptrauth (ptr @f_u8x16, i32 0, i64 34246)(<16 x i8> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <16 x i8> ptrauth (ptr @f_u8x16, i32 0)(<16 x i8> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_U8x16(U8x16([0; 16])); + // DISC: call <8 x i16> ptrauth (ptr @f_u16x8, i32 0, i64 34246)(<8 x i16> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <8 x i16> ptrauth (ptr @f_u16x8, i32 0)(<8 x i16> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_U16x8(U16x8([0; 8])); + // DISC: call <4 x i32> ptrauth (ptr @f_u32x4, i32 0, i64 34246)(<4 x i32> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <4 x i32> ptrauth (ptr @f_u32x4, i32 0)(<4 x i32> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_U32x4(U32x4([0; 4])); + // DISC: call <2 x i64> ptrauth (ptr @f_u64x2, i32 0, i64 34246)(<2 x i64> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <2 x i64> ptrauth (ptr @f_u64x2, i32 0)(<2 x i64> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_U64x2(U64x2([0; 2])); + // DISC: call <4 x float> ptrauth (ptr @f_f32x4, i32 0, i64 34246)(<4 x float> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <4 x float> ptrauth (ptr @f_f32x4, i32 0)(<4 x float> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_F32x4(F32x4([0.0; 4])); + // DISC: call <2 x double> ptrauth (ptr @f_f64x2, i32 0, i64 34246)(<2 x double> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 34246) ] + // NO_DISC: call <2 x double> ptrauth (ptr @f_f64x2, i32 0)(<2 x double> %{{.*}}) #{{.*}} [ "ptrauth"(i32 0, i64 0) ] + let _ = T_F64x2(F64x2([0.0; 2])); + } +} + +// Equivalent C sample: +// +// ```c +// typedef signed char I8x16 __attribute__((vector_size(16))); +// typedef short I16x8 __attribute__((vector_size(16))); +// typedef int I32x4 __attribute__((vector_size(16))); +// typedef long long I64x2 __attribute__((vector_size(16))); +// +// typedef unsigned char U8x16 __attribute__((vector_size(16))); +// typedef unsigned short U16x8 __attribute__((vector_size(16))); +// typedef unsigned int U32x4 __attribute__((vector_size(16))); +// typedef unsigned long long U64x2 __attribute__((vector_size(16))); +// +// typedef float F32x4 __attribute__((vector_size(16))); +// typedef double F64x2 __attribute__((vector_size(16))); +// +// extern I8x16 f_i8x16(I8x16); +// extern I16x8 f_i16x8(I16x8); +// extern I32x4 f_i32x4(I32x4); +// extern I64x2 f_i64x2(I64x2); +// extern U8x16 f_u8x16(U8x16); +// extern U16x8 f_u16x8(U16x8); +// extern U32x4 f_u32x4(U32x4); +// extern U64x2 f_u64x2(U64x2); +// extern F32x4 f_f32x4(F32x4); +// extern F64x2 f_f64x2(F64x2); +// +// typedef I8x16 (*FnI8x16)(I8x16); +// typedef I16x8 (*FnI16x8)(I16x8); +// typedef I32x4 (*FnI32x4)(I32x4); +// typedef I64x2 (*FnI64x2)(I64x2); +// typedef U8x16 (*FnU8x16)(U8x16); +// typedef U16x8 (*FnU16x8)(U16x8); +// typedef U32x4 (*FnU32x4)(U32x4); +// typedef U64x2 (*FnU64x2)(U64x2); +// typedef F32x4 (*FnF32x4)(F32x4); +// typedef F64x2 (*FnF64x2)(F64x2); +// +// FnI8x16 T_I8x16 = f_i8x16; +// FnI16x8 T_I16x8 = f_i16x8; +// FnI32x4 T_I32x4 = f_i32x4; +// FnI64x2 T_I64x2 = f_i64x2; +// FnU8x16 T_U8x16 = f_u8x16; +// FnU16x8 T_U16x8 = f_u16x8; +// FnU32x4 T_U32x4 = f_u32x4; +// FnU64x2 T_U64x2 = f_u64x2; +// FnF32x4 T_F32x4 = f_f32x4; +// FnF64x2 T_F64x2 = f_f64x2; +// +// void test(void) { +// T_I8x16((I8x16){0}); +// T_I16x8((I16x8){0}); +// T_I32x4((I32x4){0}); +// T_I64x2((I64x2){0}); +// T_U8x16((U8x16){0}); +// T_U16x8((U16x8){0}); +// T_U32x4((U32x4){0}); +// T_U64x2((U64x2){0}); +// T_F32x4((F32x4){0.0f}); +// T_F64x2((F64x2){0.0}); +// } +// ``` diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-members.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-members.rs new file mode 100644 index 0000000000000..1b36a6414ca67 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-members.rs @@ -0,0 +1,529 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. + +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 +//@ [NO_DISC] needs-llvm-components: aarch64 +//@ [NO_DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=-function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included at the end of the file). Both compilers must +// generate identical values. +// +// Check the signing of internal members of structs that are themselves function pointers. + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_std] +#![no_core] + +extern crate minicore; +use minicore::{Sync, mem, ptr}; + +// Function definitions, used as members in structs. +extern "C" fn f() {} +extern "C" fn g(i32: i32) {} +extern "C" fn h(i64: i64, j: i64) {} +extern "C" fn i(i64: i64, b: i64, c: f32) {} + +// Structs... +#[repr(transparent)] +struct A(extern "C" fn()); + +#[repr(transparent)] +struct B(extern "C" fn(i32)); + +#[repr(transparent)] +struct C(extern "C" fn(i64, i64)); + +#[repr(transparent)] +struct NotFn(u64); + +#[repr(transparent)] +struct AlsoNotFn(u64); + +// and their wrappers (L - level). +#[repr(transparent)] +struct L1A(A); + +#[repr(transparent)] +struct L1B(B); + +#[repr(transparent)] +struct L2A(L1A); + +#[repr(transparent)] +struct L2B(L1B); + +#[repr(transparent)] +struct L3A(L2A); + +#[repr(transparent)] +struct L3B(L2B); + +#[repr(transparent)] +struct L4A(L3A); + +#[repr(transparent)] +struct L4B(L3B); + +#[repr(transparent)] +struct L5A(L4A); + +#[repr(transparent)] +struct L5B(L4B); + +#[repr(C)] +struct MixedPair { + f0: extern "C" fn(), + f1: extern "C" fn(i32), +} + +#[repr(transparent)] +struct L1NotFn(NotFn); + +#[repr(transparent)] +struct L1AlsoNotFn(AlsoNotFn); + +// Make sure that static initialization traverses struct members and uses correct discriminators. +// DISC-DAG: @{{.*}}T_TREE_SRC = internal constant <{ ptr, ptr, ptr, ptr }> <{ ptr ptrauth (ptr @{{.*}}f, i32 0, i64 18983), ptr ptrauth (ptr @{{.*}}g, i32 0, i64 2712), ptr ptrauth (ptr @{{.*}}h, i32 0, i64 55265), ptr ptrauth (ptr @{{.*}}i, i32 0, i64 44485) }> +// NO_DISC-DAG: @{{.*}}T_TREE_SRC = internal constant <{ ptr, ptr, ptr, ptr }> <{ ptr ptrauth (ptr @{{.*}}f, i32 0), ptr ptrauth (ptr @{{.*}}g, i32 0), ptr ptrauth (ptr @{{.*}}h, i32 0), ptr ptrauth (ptr @{{.*}}i, i32 0) }> +// DISC-DAG: @{{.*}}T_WRAPPED_FN_PTR = internal constant ptr ptrauth (ptr @{{.*}}g, i32 0, i64 2712) +// NO_DISC-DAG: @{{.*}}T_WRAPPED_FN_PTR = internal constant ptr ptrauth (ptr @{{.*}}g, i32 0) + +// Simplest fn ptr resign through a struct transmute. +#[inline(never)] +// CHECK-DAG: test_1_struct_resign +pub fn test_1_struct_resign() { + let a: A = A(f); + // DISC: [[PTR_RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @{{.*}}f, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 0, i64 2712) + // DISC: [[INT_TO_PTR:%.*]] = inttoptr i64 [[PTR_RESIGNED]] to ptr + // DISC: store ptr [[INT_TO_PTR]], ptr [[PTR_STORED:%*.]] + // NO_DISC-NOT: call i64 @llvm.ptrauth.resign + // NO_DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0), ptr [[STORE:%.*]], align 8 + let b: B = unsafe { mem::transmute(a) }; + + unsafe { + // DISC: [[PTR_RELOADED:%.*]] = load ptr, ptr [[PTR_STORED]] + // NO_DISC: [[LOAD:%.*]] = load ptr, ptr [[STORE]] + ptr::read_volatile(&b); + // DISC: call void [[PTR_RELOADED]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 2712) ] + // NO_DISC: call void [[LOAD]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (b.0)(42); + } +} + +// Same as above but in a deep chain, expect the chain to disappear. +#[inline(never)] +// CHECK-DAG: test_2_deep_nested +pub fn test_2_deep_nested() { + let a = L5A(L4A(L3A(L2A(L1A(A(f)))))); + // DISC: [[PTR_RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @{{.*}}f, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 0, i64 2712) + // DISC: [[INT_TO_PTR:%.*]] = inttoptr i64 [[PTR_RESIGNED]] to ptr + // DISC: store ptr [[INT_TO_PTR]], ptr [[PTR_STORED:%*.]] + // NO_DISC-NOT: call i64 @llvm.ptrauth.resign + // NO_DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0), ptr [[STORE:%.*]], align 8 + let b: L5B = unsafe { mem::transmute(a) }; + + unsafe { + // DISC: [[PTR_RELOADED:%.*]] = load ptr, ptr [[PTR_STORED]] + // NO_DISC: [[LOAD:%.*]] = load ptr, ptr [[STORE]] + ptr::read_volatile(&b); + // DISC: call void [[PTR_RELOADED]](i32 4242) {{.*}} [ "ptrauth"(i32 0, i64 2712) ] + // NO_DISC: call void [[LOAD]](i32 4242) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (b.0.0.0.0.0.0)(4242); + } +} + +// Different destination discriminator. +#[inline(never)] +// CHECK-DAG: test_3_cross_fnptr_cast +pub fn test_3_cross_fnptr_cast() { + let a: A = A(f); + // DISC: [[PTR_RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @{{.*}}f, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 0, i64 55265) + // DISC: [[INT_TO_PTR:%.*]] = inttoptr i64 [[PTR_RESIGNED]] to ptr + // DISC: store ptr [[INT_TO_PTR]], ptr [[PTR_STORED:%*.]] + // NO_DISC-NOT: call i64 @llvm.ptrauth.resign + // NO_DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0), ptr [[STORE:%.*]], align 8 + let c: C = unsafe { mem::transmute(a) }; + + unsafe { + // DISC: [[PTR_RELOADED:%.*]] = load ptr, ptr [[PTR_STORED]] + // NO_DISC: [[LOAD:%.*]] = load ptr, ptr [[STORE]] + ptr::read_volatile(&c); + // DISC: call void [[PTR_RELOADED]](i64 1, i64 2) {{.*}} [ "ptrauth"(i32 0, i64 55265) ] + // NO_DISC: call void [[LOAD]](i64 1, i64 2) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (c.0)(1, 2); + } +} + +// Negative control. +#[inline(never)] +// CHECK-DAG: test_4_non_fnptr_cast +pub fn test_4_non_fnptr_cast() { + // CHECK-NOT: llvm.ptrauth.resign + // CHECK-NOT: ptrauth + let x = L1NotFn(NotFn(123)); + let y: L1AlsoNotFn = unsafe { mem::transmute(x) }; + + unsafe { + ptr::read_volatile(&y); + } +} + +// Mixed resigned and non-resigned fields. +#[inline(never)] +// CHECK-DAG: test_5_mixed_fnptr_cast_resign +pub fn test_5_mixed_fnptr_cast_resign() { + // Allocate the MixedPair. + // DISC: [[M:%.*]] = alloca [16 x i8] + let mut m = MixedPair { + // Resign fn() -> fn(i32). + // DISC: [[RESIGNED:%.*]] = call i64 @llvm.ptrauth.resign(i64 ptrtoint (ptr ptrauth (ptr @{{.*}}f, i32 0, i64 18983) to i64), i32 0, i64 18983, i32 0, i64 2712) + // DISC: [[F1:%.*]] = inttoptr i64 [[RESIGNED]] to ptr + // Store first struct member. + // DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0, i64 18983), ptr [[M]] + // Compute address of second member and store it + // DISC: [[M1:%.*]] = getelementptr inbounds i8, ptr [[M]], i64 8 + // DISC: store ptr [[F1]], ptr [[M1]], align 8 + // NO_DISC-NOT: call i64 @llvm.ptrauth.resign + // NO_DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0), ptr [[M:%.*]], align 8 + // NO_DISC: [[M_0:%.*]] = getelementptr inbounds i8, ptr [[M]], i64 8 + // NO_DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0), ptr [[M_0]], align 8 + f0: f, + f1: unsafe { mem::transmute::(f) }, + }; + + // Volatile read of the whole struct. + // DISC: [[PAIR:%.*]] = call { ptr, ptr } @{{.*}}read_volatile + // NO_DISC: [[PAIR:%.*]] = call { ptr, ptr } @{{.*}}read_volatile + let tmp = unsafe { ptr::read_volatile(&m) }; + + // Extract both fields and call each of them + // DISC: [[TMP0:%.*]] = extractvalue { ptr, ptr } [[PAIR]], 0 + // DISC: [[TMP1:%.*]] = extractvalue { ptr, ptr } [[PAIR]], 1 + // DISC: call void [[TMP0]]() {{.*}} "ptrauth"(i32 0, i64 18983) + // DISC: call void [[TMP1]](i32 123) {{.*}} "ptrauth"(i32 0, i64 2712) + // NO_DISC: [[TMP0:%.*]] = extractvalue { ptr, ptr } [[PAIR]], 0 + // NO_DISC: [[TMP1:%.*]] = extractvalue { ptr, ptr } [[PAIR]], 1 + // NO_DISC: call void {{.*}}() {{.*}} [ "ptrauth"(i32 0, i64 0) ] + // NO_DISC: call void {{.*}}(i32 123) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (tmp.f0)(); + (tmp.f1)(123); +} + +// Aggregate reinterpretation (the whole Struct, not just a Member) with mixed members. +#[inline(never)] +// CHECK-DAG: test_6_mixed_layout_cast +pub fn test_6_mixed_layout_cast() { + let x = (A(f), NotFn(999)); + // DISC: [[Y:%.*]] = alloca [16 x i8] + // DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0, i64 18983), ptr [[Y]] + // NO_DISC: [[Y:%.*]] = alloca [16 x i8] + // NO_DISC: store ptr ptrauth (ptr @{{.*}}f, i32 0), ptr [[Y]] + let y: (B, AlsoNotFn) = unsafe { mem::transmute(x) }; + + unsafe { + ptr::read_volatile(&y); + // DISC: [[Y_LOAD:%.*]] = load ptr, ptr [[Y]] + // DISC: call void [[Y_LOAD]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 2712) ] + // NO_DISC: [[Y_LOAD:%.*]] = load ptr, ptr [[Y]] + // NO_DISC: call void [[Y_LOAD]](i32 42) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (y.0.0)(42); + } +} + +impl Sync for RootSrc {} +impl Sync for RootDst {} + +#[repr(C)] +struct RootSrc { + f0: extern "C" fn(), + f1: extern "C" fn(i32), + f2: extern "C" fn(i64, i64), + f3: extern "C" fn(i64, i64, f32), +} + +type G0 = extern "C" fn(i32); +type G1 = extern "C" fn(i64, i64); +type G2 = extern "C" fn(i64, i64, f32); +type G3 = extern "C" fn(); + +#[repr(C)] +struct RootDst { + f0: G0, + f1: G1, + f2: G2, + f3: G3, +} + +static T_TREE_SRC: RootSrc = RootSrc { f0: f, f1: g, f2: h, f3: i }; + +#[inline(never)] +// Aggregate stress test, multiple resigning. +// CHECK-DAG: test_7_tree_cast_mixed +pub fn test_7_tree_cast_mixed() { + let src: RootSrc = unsafe { ptr::read_volatile(&T_TREE_SRC) }; + let dst = RootDst { + // field 0: load -> resign(18983 -> 2712) -> store + // DISC: [[SRC0:%.*]] = load ptr, ptr [[SRC:%.*]], + // DISC: [[SRC0I:%.*]] = ptrtoint ptr [[SRC0]] to i64 + // DISC: [[RESIGN0:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SRC0I]], i32 0, i64 18983, i32 0, i64 2712) + // DISC: [[DST0:%.*]] = inttoptr i64 [[RESIGN0]] to ptr + f0: unsafe { mem::transmute::(src.f0) }, + // field 1: load -> resign(2712 -> 55265) -> store + // DISC: [[SRC1PTR:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 8 + // DISC: [[SRC1:%.*]] = load ptr, ptr [[SRC1PTR]] + // DISC: [[SRC1I:%.*]] = ptrtoint ptr [[SRC1]] to i64 + // DISC: [[RESIGN1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SRC1I]], i32 0, i64 2712, i32 0, i64 55265) + // DISC: [[DST1:%.*]] = inttoptr i64 [[RESIGN1]] to ptr + f1: unsafe { mem::transmute::(src.f1) }, + // field 2: load -> resign(5526 -> 44485) -> store + // DISC: [[SRC2PTR:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 16 + // DISC: [[SRC2:%.*]] = load ptr, ptr [[SRC2PTR]] + // DISC: [[SRC2I:%.*]] = ptrtoint ptr [[SRC2]] to i64 + // DISC: [[RESIGN2:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SRC2I]], i32 0, i64 55265, i32 0, i64 44485) + // DISC: [[DST2:%.*]] = inttoptr i64 [[RESIGN2]] to ptr + f2: unsafe { mem::transmute::(src.f2) }, + // field 3: load -> resign(44485 -> 18983) -> store + // DISC: [[SRC3PTR:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 24 + // DISC: [[SRC3:%.*]] = load ptr, ptr [[SRC3PTR]] + // DISC: [[SRC3I:%.*]] = ptrtoint ptr [[SRC3]] to i64 + // DISC: [[RESIGN3:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[SRC3I]], i32 0, i64 44485, i32 0, i64 18983) + // DISC: [[DST3:%.*]] = inttoptr i64 [[RESIGN3]] to ptr + f3: unsafe { mem::transmute::(src.f3) }, + // DISC: store ptr [[DST0]], ptr [[DST:%.*]], + // DISC: [[DST1PTR:%.*]] = getelementptr inbounds i8, ptr %dst, i64 8 + // DISC: store ptr [[DST1]], ptr [[DST1PTR]] + // DISC: [[DST2PTR:%.*]] = getelementptr inbounds i8, ptr %dst, i64 16 + // DISC: store ptr [[DST2]], ptr [[DST2PTR]] + // DISC: [[DST3PTR:%.*]] = getelementptr inbounds i8, ptr %dst, i64 24 + // DISC: store ptr [[DST3]], ptr [[DST3PTR]] + }; + + unsafe { + ptr::read_volatile(&dst); + } + // NO_DISC-NOT: call i64 @llvm.ptrauth.resign + + // Field loads and authed calls: + // DISC: [[CALL0:%.*]] = load ptr, ptr [[DST]] + // DISC: call void [[CALL0]](i32 1) {{.*}} [ "ptrauth"(i32 0, i64 2712) ] + // NO_DISC: call void {{.*}}(i32 1) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (dst.f0)(1); + // DISC: [[CALL1PTR:%.*]] = getelementptr inbounds i8, ptr [[DST]], i64 8 + // DISC: [[CALL1:%.*]] = load ptr, ptr [[CALL1PTR]], + // DISC: call void [[CALL1]](i64 2, i64 3) {{.*}} [ "ptrauth"(i32 0, i64 55265) ] + // NO_DISC: call void {{.*}}(i64 2, i64 3) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (dst.f1)(2, 3); + // DISC: [[CALL2PTR:%.*]] = getelementptr inbounds i8, ptr [[DST]], i64 16 + // DISC: [[CALL2:%.*]] = load ptr, ptr [[CALL2PTR]], + // DISC: call void [[CALL2]](i64 4, i64 5, float 6.000000e+00) {{.*}} [ "ptrauth"(i32 0, i64 44485) ] + // NO_DISC: call void {{.*}}(i64 4, i64 5, float 6.000000e+00) {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (dst.f2)(4, 5, 6.0); + // DISC: [[CALL3PTR:%.*]] = getelementptr inbounds i8, ptr [[DST]], i64 24 + // DISC: [[CALL3:%.*]] = load ptr, ptr [[CALL3PTR]], + // DISC: call void [[CALL3]]() {{.*}} [ "ptrauth"(i32 0, i64 18983) ] + // NO_DISC: call void {{.*}}() {{.*}} [ "ptrauth"(i32 0, i64 0) ] + (dst.f3)(); +} + +#[repr(transparent)] +struct Wrapper(extern "C" fn(i32)); + +impl Sync for Wrapper {} + +static T_WRAPPED_FN_PTR: Wrapper = Wrapper(g); + +// C equivalent. +// #include +// +// typedef void (*fn0)(void); +// typedef void (*fn1)(int); +// typedef void (*fn2)(long long, long long); +// typedef void (*fn3)(long long, long long, float); +// +// typedef void (*g0)(int); +// typedef void (*g1)(long long, long long); +// typedef void (*g2)(long long, long long, float); +// typedef void (*g3)(void); +// +// void f(void); +// void g(int); +// void h(long long, long long); +// void i(long long, long long, float); +// +// typedef struct { +// fn0 f; +// } A; +// typedef struct { +// fn1 f; +// } B; +// typedef struct { +// fn2 f; +// } C; +// typedef struct { +// A a; +// } L1A; +// typedef struct { +// B b; +// } L1B; +// typedef struct { +// L1A a; +// } L2A; +// typedef struct { +// L1B b; +// } L2B; +// typedef struct { +// L2A a; +// } L3A; +// typedef struct { +// L2B b; +// } L3B; +// typedef struct { +// L3A a; +// } L4A; +// typedef struct { +// L3B b; +// } L4B; +// typedef struct { +// L4A a; +// } L5A; +// typedef struct { +// L4B b; +// } L5B; +// typedef struct { +// fn0 f0; +// fn1 f1; +// } MixedPair; +// typedef struct { +// uint64_t x; +// } NotFn; +// typedef struct { +// uint64_t x; +// } AlsoNotFn; +// typedef struct { +// NotFn n; +// } L1NotFn; +// typedef struct { +// AlsoNotFn a; +// } L1AlsoNotFn; +// +// __attribute__((noinline)) void test_1_struct_resign(void) { +// A a; +// a.f = f; +// +// B b; +// b.f = (fn1)a.f; +// +// volatile B tmp = b; +// b.f(42); +// } +// +// __attribute__((noinline)) void test_2_deep_nested(void) { +// L5A a; +// a.a.a.a.a.a.f = f; +// +// L5B b; +// b.b.b.b.b.b.f = (fn1)a.a.a.a.a.a.f; +// +// volatile L5B tmp = b; +// b.b.b.b.b.b.f(42); +// } +// +// __attribute__((noinline)) void test_3_cross_fnptr_cast(void) { +// A a; +// a.f = f; +// +// C c; +// c.f = (fn2)a.f; +// +// volatile C tmp = c; +// c.f(1, 2); +// } +// +// __attribute__((noinline)) void test_4_non_fnptr_cast(void) { +// L1NotFn x; +// x.n.x = 123; +// +// L1AlsoNotFn y; +// y.a = *(AlsoNotFn *)&x; +// +// volatile L1AlsoNotFn tmp = y; +// } +// +// __attribute__((noinline)) void test_5_mixed_fnptr_cast_resign(void) { +// MixedPair m; +// m.f0 = f; +// m.f1 = (fn1)f; +// +// volatile MixedPair tmp = m; +// m.f0(); +// m.f1(123); +// } +// +// typedef struct { +// A a; +// NotFn n; +// } TupleA; +// +// typedef struct { +// B b; +// AlsoNotFn n; +// } TupleB; +// +// __attribute__((noinline)) void test_6_mixed_layout_cast(void) { +// TupleA x; +// x.a.f = f; +// x.n.x = 999; +// +// TupleB y = *(TupleB *)&x; +// +// volatile TupleB tmp = y; +// +// y.b.f(42); +// } +// +// typedef struct { +// fn0 f0; +// fn1 f1; +// fn2 f2; +// fn3 f3; +// } RootSrc; +// +// typedef struct { +// g0 f0; +// g1 f1; +// g2 f2; +// g3 f3; +// } RootDst; +// +// static const RootSrc T_TREE_SRC = { +// .f0 = f, +// .f1 = g, +// .f2 = h, +// .f3 = i, +// }; +// +// __attribute__((noinline)) void test_7_tree_cast_mixed(void) { +// volatile const RootSrc *vp = &T_TREE_SRC; +// +// RootSrc src = *vp; +// +// RootDst dst = { +// .f0 = (g0)src.f0, +// .f1 = (g1)src.f1, +// .f2 = (g2)src.f2, +// .f3 = (g3)src.f3, +// }; +// +// volatile RootDst tmp = dst; +// +// dst.f0(1); +// dst.f1(2, 3); +// dst.f2(4, 5, 6.0f); +// dst.f3(); +// } diff --git a/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-name.rs b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-name.rs new file mode 100644 index 0000000000000..f8e4112ec94d2 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-fn-ptr-type-discrimination-struct-name.rs @@ -0,0 +1,100 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +// Run it at O0, so that the compiler doesn't optimise the calls away. + +//@ revisions: DISC NO_DISC +//@ [DISC] needs-llvm-components: aarch64 +//@ [DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest --crate-type=lib -Zpointer-authentication=+function-pointer-type-discrimination -C opt-level=0 + +// Test generation of function-pointer type discriminators. The discriminator values were obtained +// from Clang by compiling equivalent C code (included). Both compilers must generate identical +// values. +// +// Make sure that rust only uses the final part of struct's name (`Foo` or `Bar`), so that the +// discriminators are `F3FooE` and `F3BarE`, not using def path for the base of encoding. + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] +extern crate minicore; +use minicore::hint::black_box; + +#[repr(C)] +pub struct Foo { + x: i32, +} + +#[repr(C)] +pub struct Bar { + x: i32, +} + +extern "C" fn takes_foo(_: Foo) {} +extern "C" fn takes_bar(_: Bar) {} + +#[used] +// DISC-DAG: @{{.*}}FOO_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes_foo, i32 0, i64 58649) +// Without type discriminators all the functions are the same, so compiler is able to use both +// takes_foo/take_bar. +// NO_DISC-DAG: @{{.*}}FOO_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes_{{foo|bar}}, i32 0) +static FOO_FNPTR: extern "C" fn(Foo) = takes_foo; + +#[used] +// DISC-DAG: @{{.*}}BAR_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes_bar, i32 0, i64 41614) +// NO_DISC-DAG: @{{.*}}BAR_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes_{{foo|bar}}, i32 0) +static BAR_FNPTR: extern "C" fn(Bar) = takes_bar; + +// While not possible to express in C, we could force it through C++ path with something along the +// lines of: +// ```c++ +// namespace a { +// struct SameName { +// int x; +// }; +// +// void takes(SameName) {} +// } +// +// namespace b { +// struct SameName { +// int x; +// }; +// +// void takes(SameName) {} +// } +// +// void (*a_fnptr)(a::SameName) = a::takes; +// void (*b_fnptr)(b::SameName) = b::takes; +// ``` +// Make sure that Rust uses `Fv8SameNameE` for both `A_FNPTR` and `B_FNPTR`, not +// `Fv11a::SameNameE`, or `Fv11b::SameNameE`. + +mod a { + #[repr(C)] + pub struct SameName { + pub x: i32, + } + + pub extern "C" fn takes(_: SameName) {} +} + +mod b { + #[repr(C)] + pub struct SameName { + pub x: i32, + } + + pub extern "C" fn takes(_: SameName) {} +} + +#[used] +// DISC-DAG: @{{.*}}A_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes, i32 0, i64 57535) +// NO_DISC-DAG: @{{.*}}A_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes{{.*}}, i32 0) +static A_FNPTR: extern "C" fn(a::SameName) = a::takes; + +#[used] +// DISC-DAG: @{{.*}}B_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes, i32 0, i64 57535) +// NO_DISC-DAG: @{{.*}}B_FNPTR = constant ptr ptrauth (ptr @{{.*}}takes{{.*}}, i32 0) +static B_FNPTR: extern "C" fn(b::SameName) = b::takes; diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs index d457973b690f2..7c327a0b0ee80 100644 --- a/tests/codegen-llvm/pauth/pauth-init-fini.rs +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -1,7 +1,7 @@ //@ add-minicore // ignore-tidy-linelength //@ only-pauthtest -//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ADDR-DISC O3_PAUTH-ADDR-DISC O0_PAUTH-NO-INIT-FINI O3_PAUTH-NO-INIT-FINI +//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ADDR-DISC O3_PAUTH-ADDR-DISC O0_PAUTH-NO-INIT-FINI O3_PAUTH-NO-INIT-FINI O0_PAUTH-INIT-FINI-FN-TY-DISC O3_PAUTH-INIT-FINI-FN-TY-DISC //@ [O0_PAUTH] needs-llvm-components: aarch64 //@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 @@ -15,9 +15,13 @@ //@ [O3_PAUTH-ADDR-DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 -Zpointer-authentication=+init-fini-address-discrimination //@ [O3_PAUTH-NO-INIT-FINI] needs-llvm-components: aarch64 //@ [O3_PAUTH-NO-INIT-FINI] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Zpointer-authentication=-init-fini - +//@ [O0_PAUTH-INIT-FINI-FN-TY-DISC] needs-llvm-components: aarch64 +//@ [O0_PAUTH-INIT-FINI-FN-TY-DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Zpointer-authentication=+init-fini,+function-pointer-type-discrimination,-init-fini-address-discrimination +//@ [O3_PAUTH-INIT-FINI-FN-TY-DISC] needs-llvm-components: aarch64 +//@ [O3_PAUTH-INIT-FINI-FN-TY-DISC] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 -Zpointer-authentication=+init-fini,+function-pointer-type-discrimination,-init-fini-address-discrimination // Make sure that init/fini metadata uses correct discriminator: 0xd9d4/55764 - ptrauth_string_discriminator("init_fini"). // And that address discriminator can be enabled. +// Function pointer type discrimination does not apply to init/fini entries. #![feature(no_core, lang_items)] #![no_std] @@ -33,6 +37,8 @@ use minicore::*; // O3_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}init_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7init_fn), section ".init_array.90" // O0_PAUTH-NO-INIT-FINI-NOT: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth // O0_PAUTH-NO-INIT-FINI-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_INIT = constant ptr ptrauth +// O0_PAUTH-INIT-FINI-FN-TY-DISC: @{{[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" +// O3_PAUTH-INIT-FINI-FN-TY-DISC: @{{[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" #[used] #[link_section = ".init_array.90"] static GLOBAL_INIT: extern "C" fn() = init_fn; @@ -43,6 +49,8 @@ static GLOBAL_INIT: extern "C" fn() = init_fn; // O3_PAUTH-ADDR-DISC: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth (ptr @{{[0-9A-Za-z_]+}}fini_fn, i32 0, i64 55764, ptr @_RNvCsf7kshQi9mOB_15pauth_init_fini7fini_fn), section ".fini_array.90" // O0_PAUTH-NO-INIT-FINI-NOT: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth // O3_PAUTH-NO-INIT-FINI-NOT: @{{[0-9A-Za-z_]+}}GLOBAL_FINI = constant ptr ptrauth +// O0_PAUTH-INIT-FINI-FN-TY-DISC: @{{[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" +// O3_PAUTH-INIT-FINI-FN-TY-DISC: @{{[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" #[used] #[link_section = ".fini_array.90"] static GLOBAL_FINI: extern "C" fn(i32) = fini_fn; diff --git a/tests/run-make/pauth-drop-terminator/before_instcombine.check b/tests/run-make/pauth-drop-terminator/before_instcombine.check new file mode 100644 index 0000000000000..50b46ecf4ae2e --- /dev/null +++ b/tests/run-make/pauth-drop-terminator/before_instcombine.check @@ -0,0 +1,4 @@ +// CHECK-LABEL: ; *** IR Dump Before InstCombinePass + +// CHECK-LABEL: define void @{{.*}}Drop4drop +// CHECK: call void ptrauth (ptr @c_cleanup, i32 0)(i32 noundef %{{.*}}) #[[#]] [ "ptrauth"(i32 0, i64 0) ] diff --git a/tests/run-make/pauth-drop-terminator/before_instcombine_ty_disc.check b/tests/run-make/pauth-drop-terminator/before_instcombine_ty_disc.check new file mode 100644 index 0000000000000..57f2d8d91e28f --- /dev/null +++ b/tests/run-make/pauth-drop-terminator/before_instcombine_ty_disc.check @@ -0,0 +1,4 @@ +// CHECK-LABEL: ; *** IR Dump Before InstCombinePass + +// CHECK-LABEL: define void @{{.*}}Drop4drop +// CHECK: call void ptrauth (ptr @c_cleanup, i32 0, i64 2712)(i32 noundef %{{.*}}) #[[#]] [ "ptrauth"(i32 0, i64 2712) ] diff --git a/tests/run-make/pauth-drop-terminator/full_ir.check b/tests/run-make/pauth-drop-terminator/full_ir.check new file mode 100644 index 0000000000000..ad4c80c0675c4 --- /dev/null +++ b/tests/run-make/pauth-drop-terminator/full_ir.check @@ -0,0 +1,3 @@ +// CHECK-LABEL: define void @{{.*}}Drop4drop +// CHECK-NOT: call void ptrauth (ptr @c_cleanup +// CHECK: tail call void @c_cleanup diff --git a/tests/run-make/pauth-drop-terminator/main.rs b/tests/run-make/pauth-drop-terminator/main.rs new file mode 100644 index 0000000000000..45646cfd6da60 --- /dev/null +++ b/tests/run-make/pauth-drop-terminator/main.rs @@ -0,0 +1,22 @@ +extern "C" { + fn c_cleanup(x: i32); +} + +struct Bomb(i32); + +impl Drop for Bomb { + fn drop(&mut self) { + unsafe { + c_cleanup(self.0); + } + } +} + +pub fn may_unwind(x: i32) { + let b = Bomb(x); + + match b.0 { + 0 => return, + _ => {} + } +} diff --git a/tests/run-make/pauth-drop-terminator/rmake.rs b/tests/run-make/pauth-drop-terminator/rmake.rs new file mode 100644 index 0000000000000..c6d769acbb12b --- /dev/null +++ b/tests/run-make/pauth-drop-terminator/rmake.rs @@ -0,0 +1,61 @@ +// Make sure that for `aarch64-unknown-linux-pauthtest` compiler correctly signs drop terminators. +// Please note that the generated pattern: +// ```llvm +// tail call void ptrauth (ptr @c_cleanup, i32 0)(ptr @c_cleanup, i32 0, i64 2712) #2 [ "ptrauth"(i32 0, i64 2712) ] +// ``` +// is optimised out by LLVM's instcombine, hence dump the IR before that pass and inspect it. + +//@ only-pauthtest +// ignore-tidy-linelength + +use run_make_support::path_helpers::source_root; +use run_make_support::{llvm_filecheck, rfs, rustc}; + +fn main() { + let sibling = source_root().join("tests/run-make/pauth-drop-terminator"); + + let output = rustc() + .input("main.rs") + .target("aarch64-unknown-linux-pauthtest") + .opt_level("3") + .arg("--crate-type=lib") + .arg("--emit=llvm-ir") + .arg("-C") + .arg("llvm-args=-print-before=instcombine") + .run(); + + let stderr = output.stderr_utf8(); + + // -print-before outputs to stderr, so copy it over to a file, that can later be used by + // filecheck. + rfs::write("before_instcombine.ll", stderr); + + llvm_filecheck() + .patterns(sibling.join("before_instcombine.check")) + .stdin_buf(rfs::read("before_instcombine.ll")) + .run(); + + llvm_filecheck().patterns(sibling.join("full_ir.check")).stdin_buf(rfs::read("main.ll")).run(); + + // Compile again now using function pointer type discrimination. + let output = rustc() + .input("main.rs") + .target("aarch64-unknown-linux-pauthtest") + .opt_level("3") + .arg("--crate-type=lib") + .arg("--emit=llvm-ir") + .arg("-Zpointer-authentication=+function-pointer-type-discrimination") + .arg("-Cunsafe-allow-abi-mismatch=pointer-authentication") + .arg("-C") + .arg("llvm-args=-print-before=instcombine") + .run(); + + let stderr = output.stderr_utf8(); + + rfs::write("before_instcombine_ty_disc.ll", stderr); + + llvm_filecheck() + .patterns(sibling.join("before_instcombine_ty_disc.check")) + .stdin_buf(rfs::read("before_instcombine_ty_disc.ll")) + .run(); +}