From a632e29396659afaf10da84136eb7dbe2b93850d Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:01:47 +0000 Subject: [PATCH 1/8] [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 | 32 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/common.rs | 23 +++++++------ compiler/rustc_codegen_llvm/src/context.rs | 4 +-- .../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 | 8 +++-- compiler/rustc_ty_utils/src/abi.rs | 6 ++++ 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, 165 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 0a88085960e89..6b58ec7b0f0a1 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1864,6 +1864,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 6bd186f1121fc..712a1c14ef8e6 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -323,7 +323,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>, - _schema: Option<&PointerAuthSchema>, + _ptrauth_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 64f9982ac7de6..9eb507b875578 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -404,7 +404,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - _pointer_auth_schema: Option<&PointerAuthSchema>, + _ptrauth_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..10cbe1ccb8059 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_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 9d4602e49968d..839bf9a2c880f 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1582,6 +1582,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, '_> { @@ -2216,8 +2240,12 @@ 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; + // If sess().pointer_authentication_fn_ptr_type_discrimination() is true, this contains + // the function pointer type discriminator; otherwise, it is 0. + let discriminator = fn_abi?.ptrauth_discriminator; + 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 7a05bf718663c..c5c943002dfdc 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, + ptrauth_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(); @@ -54,7 +52,7 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( return llfn; } - let addr_diversity = match schema.is_address_discriminated { + let addr_diversity = match ptrauth_schema.is_address_discriminated { PointerAuthAddressDiscriminator::HardwareAddress(true) => Some(llfn), PointerAuthAddressDiscriminator::HardwareAddress(false) => None, PointerAuthAddressDiscriminator::Synthetic(val) => { @@ -63,7 +61,12 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( Some(unsafe { llvm::LLVMConstIntToPtr(llval, llty) }) } }; - const_ptr_auth(llfn, schema.key as u32, schema.constant_discriminator as u64, addr_diversity) + const_ptr_auth( + llfn, + ptrauth_schema.key as u32, + ptrauth_schema.constant_discriminator as u64, + addr_diversity, + ) } /* @@ -179,11 +182,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { &self, global_alloc: GlobalAlloc<'tcx>, need_symbol_name: bool, - schema: Option<&PointerAuthSchema>, + ptrauth_schema: Option, ) -> Result<&'ll Value, u64> { let alloc = match global_alloc { GlobalAlloc::Function { instance, .. } => { - return Ok(self.get_fn_addr(instance, schema)); + return Ok(self.get_fn_addr(instance, ptrauth_schema)); } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); @@ -405,7 +408,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { cv: Scalar, layout: abi::Scalar, llty: &'ll Type, - schema: Option<&PointerAuthSchema>, + ptrauth_schema: Option, ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { @@ -422,7 +425,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); let base_addr_space = global_alloc.address_space(self); - let base_addr = match self.alloc_to_backend(global_alloc, false, schema) { + let base_addr = match self.alloc_to_backend(global_alloc, false, ptrauth_schema) { Ok(base_addr) => base_addr, Err(base_addr) => { let val = base_addr.wrapping_add(offset.bytes()); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 9fd1728b44c72..cf14ec1c05557 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -968,7 +968,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - pointer_auth_schema: Option<&PointerAuthSchema>, + ptrauth_schema: Option, ) -> &'ll Value { // When pointer authentication metadata is provided, `get_fn_addr` will // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant @@ -983,7 +983,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 pointer_auth_schema { + match ptrauth_schema { Some(schema) => common::maybe_sign_fn_ptr(self, instance, llfn, schema), None => llfn, } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index b7b694922bcfa..64fe767410733 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -687,4 +687,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..b45b5667be6c9 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>, + ptrauth_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..3d1a931a12e83 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>, + ptrauth_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 42c44b105b197..932b3d95c276c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -95,6 +95,7 @@ pub enum PointerAuthARM8_3Key { } /// Forms of extra discrimination. +#[derive(Clone, Debug, PartialEq)] pub enum PointerAuthDiscrimination { /// No additional discrimination. None, @@ -107,6 +108,7 @@ pub enum PointerAuthDiscrimination { } /// Types of address discrimination. +#[derive(Clone, Debug)] pub enum PointerAuthAddressDiscriminator { /// Enable/disable hardware address discrimination. HardwareAddress(bool), @@ -115,6 +117,7 @@ pub enum PointerAuthAddressDiscriminator { Synthetic(u64), } +#[derive(Clone, Debug)] pub struct PointerAuthSchema { pub is_address_discriminated: PointerAuthAddressDiscriminator, pub discrimination_kind: PointerAuthDiscrimination, @@ -1193,12 +1196,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 9fe22a3a174b6..49b2b8b5f4048 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -625,12 +625,15 @@ 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_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_discriminator } = + self; f.debug_struct("FnAbi") .field("args", args) .field("ret", ret) @@ -638,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_discriminator", ptrauth_discriminator) .finish() } } @@ -950,6 +954,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 55140d2c5458d..48b7536659e7b 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -5,6 +5,7 @@ use rustc_abi::{Align, BackendRepr, ExternAbi, PointerKind, Scalar, Size}; use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::{self as hir, find_attr}; use rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs; +use rustc_middle::ptrauth::ptrauth_compute_fn_ptr_type_discriminator_for; use rustc_middle::query::Providers; use rustc_middle::ty::layout::{ FnAbiError, HasTyCtxt, HasTypingEnv, LayoutCx, LayoutOf, TyAndLayout, fn_can_unwind, @@ -610,6 +611,11 @@ fn fn_abi_new_uncached<'tcx>( determined_fn_def_id, sig.abi(), ), + ptrauth_discriminator: if tcx.sess.pointer_authentication_fn_ptr_type_discrimination() { + ptrauth_compute_fn_ptr_type_discriminator_for(tcx, sig).unwrap_or(0).into() + } 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..a99eb7cd1e830 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_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..308d3a8625638 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_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..308d3a8625638 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_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..308d3a8625638 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_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..a99eb7cd1e830 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_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..308d3a8625638 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_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 6242d93b09534..ae0edfd7369a2 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -106,6 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -187,6 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -258,6 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -336,6 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -402,6 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -481,6 +486,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -554,6 +560,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -626,6 +633,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -692,6 +700,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -764,6 +773,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -830,6 +840,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -924,6 +935,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 176c68ecd4c7b..ed20ec1ef3283 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -106,6 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -187,6 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -258,6 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -336,6 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -402,6 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -481,6 +486,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -554,6 +560,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -626,6 +633,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -692,6 +700,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -764,6 +773,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -830,6 +840,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -924,6 +935,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 176c68ecd4c7b..ed20ec1ef3283 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -106,6 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -187,6 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -258,6 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -336,6 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -402,6 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -481,6 +486,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -554,6 +560,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -626,6 +633,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -692,6 +700,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -764,6 +773,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -830,6 +840,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -924,6 +935,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_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 65818feab4297..927594534d9b8 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 @@ -69,6 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -146,6 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -223,6 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -300,6 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -377,6 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -454,6 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_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 cbe389c42d40a..113ad20e16bc4 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 @@ -69,6 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -146,6 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -223,6 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -300,6 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -377,6 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -454,6 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_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 efeec0d86982b..e03828c2e78a4 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_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_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..ed8fe5b83fe7c 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_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..7f25ffc3c4481 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_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 d5da912a9b89a..150a9262b0f88 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.win.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.win.stderr @@ -66,6 +66,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_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..1705d3ca7509b 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_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_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_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:44:1 | From aff0d9a483b4706137efcee852493b16aa7e2e74 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 20 Aug 2026 06:47:34 +0000 Subject: [PATCH 2/8] [PAC] Use Option in FnAbi's discriminator field --- compiler/rustc_codegen_gcc/src/int.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 7 +++--- compiler/rustc_target/src/callconv/mod.rs | 4 ++-- compiler/rustc_ty_utils/src/abi.rs | 4 ++-- tests/ui/abi/c-zst.aarch64-darwin.stderr | 2 +- tests/ui/abi/c-zst.powerpc-linux.stderr | 2 +- tests/ui/abi/c-zst.s390x-linux.stderr | 2 +- tests/ui/abi/c-zst.sparc64-linux.stderr | 2 +- tests/ui/abi/c-zst.x86_64-linux.stderr | 2 +- .../ui/abi/c-zst.x86_64-pc-windows-gnu.stderr | 2 +- tests/ui/abi/debug.generic.stderr | 24 +++++++++---------- tests/ui/abi/debug.loongarch64.stderr | 24 +++++++++---------- tests/ui/abi/debug.riscv64.stderr | 24 +++++++++---------- .../x86-64-sysv64-arg-ext.apple.stderr | 12 +++++----- .../x86-64-sysv64-arg-ext.other.stderr | 12 +++++----- tests/ui/abi/pass-indirectly-attr.stderr | 4 ++-- tests/ui/abi/sysv64-zst.stderr | 2 +- .../pass-by-value-abi.aarch64.stderr | 2 +- .../c-variadic/pass-by-value-abi.win.stderr | 2 +- .../pass-by-value-abi.x86_64.stderr | 6 ++--- 20 files changed, 71 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index 10cbe1ccb8059..fe0654c665c76 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -375,7 +375,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { fixed_count: 3, conv: CanonAbi::C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, }; 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 839bf9a2c880f..c8c4a7ef4fb6c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2242,9 +2242,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // discussion in the rust-lang issue: let key: u32 = self.sess().pointer_authentication_fn_ptr_key().unwrap() as u32; - // If sess().pointer_authentication_fn_ptr_type_discrimination() is true, this contains - // the function pointer type discriminator; otherwise, it is 0. - let discriminator = fn_abi?.ptrauth_discriminator; + // If sess().pointer_authentication_fn_ptr_type_discrimination() is enabled, this contains + // the function pointer type discriminator; otherwise, it is None. LLVM expects a u64 here, + // so use 0 when no discriminator is present. + let discriminator = fn_abi?.ptrauth_discriminator.unwrap_or(0); Some(llvm::OperandBundleBox::new( "ptrauth", diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 49b2b8b5f4048..da8b5c677a0a0 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -626,7 +626,7 @@ pub struct FnAbi<'a, Ty> { /// 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_discriminator: u64, + pub ptrauth_discriminator: Option, } // Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl. @@ -954,6 +954,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(ArgAbi<'_, usize>, 56); - static_assert_size!(FnAbi<'_, usize>, 88); + static_assert_size!(FnAbi<'_, usize>, 96); // tidy-alphabetical-end } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 48b7536659e7b..c5fb2e91a0a04 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -612,9 +612,9 @@ fn fn_abi_new_uncached<'tcx>( sig.abi(), ), ptrauth_discriminator: if tcx.sess.pointer_authentication_fn_ptr_type_discrimination() { - ptrauth_compute_fn_ptr_type_discriminator_for(tcx, sig).unwrap_or(0).into() + Some(ptrauth_compute_fn_ptr_type_discriminator_for(tcx, sig).unwrap_or(0).into()) } else { - 0 + None }, }; fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi()); diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index a99eb7cd1e830..e7cb6199ab45c 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -59,7 +59,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 308d3a8625638..437ebd63ceba5 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -70,7 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 308d3a8625638..437ebd63ceba5 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -70,7 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 308d3a8625638..437ebd63ceba5 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -70,7 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 a99eb7cd1e830..e7cb6199ab45c 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -59,7 +59,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 308d3a8625638..437ebd63ceba5 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,7 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index ae0edfd7369a2..82c469e0f4f80 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -106,7 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:31:1 | @@ -188,7 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | @@ -260,7 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:40:1 | @@ -339,7 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -406,7 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:59:1 | @@ -486,7 +486,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -560,7 +560,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:62:1 | @@ -633,7 +633,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -700,7 +700,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:65:1 | @@ -773,7 +773,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -840,7 +840,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:69:1 | @@ -935,7 +935,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index ed20ec1ef3283..b5c73d00564c6 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -106,7 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:31:1 | @@ -188,7 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | @@ -260,7 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:40:1 | @@ -339,7 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -406,7 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:59:1 | @@ -486,7 +486,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -560,7 +560,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:62:1 | @@ -633,7 +633,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -700,7 +700,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:65:1 | @@ -773,7 +773,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -840,7 +840,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:69:1 | @@ -935,7 +935,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index ed20ec1ef3283..b5c73d00564c6 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -106,7 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:31:1 | @@ -188,7 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | @@ -260,7 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:40:1 | @@ -339,7 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -406,7 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:59:1 | @@ -486,7 +486,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -560,7 +560,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:62:1 | @@ -633,7 +633,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -700,7 +700,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:65:1 | @@ -773,7 +773,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -840,7 +840,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:69:1 | @@ -935,7 +935,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 927594534d9b8..df49fbfe3d272 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 @@ -69,7 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -147,7 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -225,7 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -303,7 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -381,7 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -459,7 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 113ad20e16bc4..7ceb6a2092af2 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 @@ -69,7 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -147,7 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -225,7 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -303,7 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -381,7 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -459,7 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 e03828c2e78a4..935ab97647321 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -83,7 +83,7 @@ error: fn_abi_of(extern_c) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/pass-indirectly-attr.rs:20:1 | @@ -175,7 +175,7 @@ error: fn_abi_of(extern_rust) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/pass-indirectly-attr.rs:27:1 | diff --git a/tests/ui/abi/sysv64-zst.stderr b/tests/ui/abi/sysv64-zst.stderr index ed8fe5b83fe7c..f19480faec4fa 100644 --- a/tests/ui/abi/sysv64-zst.stderr +++ b/tests/ui/abi/sysv64-zst.stderr @@ -61,7 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 7f25ffc3c4481..c616e45a9ab8e 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -70,7 +70,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 150a9262b0f88..638a0856b7b7f 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.win.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.win.stderr @@ -66,7 +66,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $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 1705d3ca7509b..776d0a72287f9 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,7 +70,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/pass-by-value-abi.rs:27:1 | @@ -151,7 +151,7 @@ error: fn_abi_of(take_va_list_sysv64) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/pass-by-value-abi.rs:37:1 | @@ -232,7 +232,7 @@ error: fn_abi_of(take_va_list_win64) = FnAbi { Win64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/pass-by-value-abi.rs:44:1 | From a2faa9f273c31e414ca7655365bd4e1d5530ab2a Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 10 Sep 2026 13:33:36 +0000 Subject: [PATCH 3/8] [PAC] repr(transparent) and correct handling of Option in encoder Also tighten the handling of enums. --- .../rustc_middle/src/ptrauth/discriminator.rs | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index 7501f1b072188..30226b8eebe07 100644 --- a/compiler/rustc_middle/src/ptrauth/discriminator.rs +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -81,6 +81,7 @@ use rustc_session::PointerAuthSchema; use rustc_span::sym; use crate::ptrauth::llvm_siphash::llvm_pointer_auth_stable_siphash; +use crate::ty::layout::LayoutCx; /// Types that can serve as a source for function pointer type discrimination. /// @@ -317,35 +318,46 @@ enum ClangDiscTy<'tcx> { Void, } -// Canonicalize Option-wrapped pointer types used to model C nullable pointers. +// Canonicalize types that are ABI-compatible with C's nullable pointer +// convention, so the rest of this encoder can treat them like the corresponding +// plain pointer type. // -// Rust and Clang should compute identical discriminators for equivalent C APIs. -// Clang does not distinguish nullable from non-nullable pointer types when -// computing function pointer authentication discriminators, so -// `Option` and `Option<*mut T>` are encoded identically to their -// underlying pointer types. +// Rust guarantees the null-pointer optimization for references, function +// pointers, Box, NonNull, and NonZero*. `Option` and `Option<&T>` are +// therefore unwrapped here. `Option<*mut T>` and `Option<*const T>` are +// deliberately left unchanged: raw pointers are not covered by the NPO +// guarantee and are handled by the general `Adt` arm in `to_clang_disc_ty`. // -// Although `Option<*mut T>` is not considered FFI-safe by Rust and triggers the -// `improper_ctypes`/`improper_ctypes_definitions` lints, this is a warning -// rather than a hard error. Canonicalizing it here preserves Clang-compatible -// discriminator computation. -// -// Please see the following tests for sample use cases: -// pauth-fn-ptr-type-discrimination-option-callback.rs, -// pauth-fn-ptr-type-discrimination-option-return.rs and pauth-fn-ptr-type-discrimination-option.rs -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); - - match inner.kind() { - ty::FnPtr(..) | ty::RawPtr(..) => return inner, - _ => {} +// Also peels `repr(transparent)` wrappers to canonicalize them to their +// underlying type. +fn canonicalize_c_type<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> { + loop { + let before = ty; + + if let ty::Adt(def, args) = ty.kind() + && tcx.is_diagnostic_item(sym::Option, def.did()) + { + let inner = args.type_at(0); + if let ty::FnPtr(..) | ty::Ref(..) = inner.kind() { + ty = inner; + } } - } - ty + // Only ADTs can be repr(transparent); skip the layout query entirely + // for everything else. + if matches!(ty.kind(), ty::Adt(..)) { + let typing_env = ty::TypingEnv::fully_monomorphized(); + + if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) { + let cx = LayoutCx::new(tcx, typing_env); + ty = layout.peel_transparent_wrappers(&cx).ty; + } + } + + if ty == before { + return ty; + } + } } /// Lowers a Rust type into a Clang-compatible discriminator type. @@ -388,8 +400,14 @@ fn to_clang_disc_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ClangDiscTy<'tcx> // arrays ignore size ty::Array(elem, _) => ClangDiscTy::Array { elem: *elem }, - // enums to integer collapse - ty::Adt(def, _) if def.is_enum() => ClangDiscTy::EnumLikeInt, + // enums to integer collapse - mirrors Clang's Type::Enum handling, + // which recurses into the enum's underlying integer type per C11 + // 6.7.2.2p4. + // A non-niche, data-carrying enum (e.g. Option<*mut T>) is not an + // "enumerated type" in the C11 sense, such enums fall through to the + // general Adt(_) => AdtName(..) arm below instead. + ty::Adt(def, _) if def.is_enum() && def.is_payloadfree() => ClangDiscTy::EnumLikeInt, + // simd vectors ty::Adt(def, args) if def.repr().simd() => { // Clang encodes SIMD vectors by their total size From a448c78c26408c21a00d368a064f4ee5d48b27a7 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:16:40 +0000 Subject: [PATCH 4/8] [PAC] Enable support for FPTR_TYPE_DISCR in ABI Version Also remove error messages/tests that used to guarded it. --- compiler/rustc_session/src/diagnostics.rs | 6 ------ compiler/rustc_session/src/session.rs | 21 +++++-------------- ...on_not_supported_pointer_authentication.rs | 12 ----------- ...ot_supported_pointer_authentication.stderr | 4 ---- 4 files changed, 5 insertions(+), 38 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/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index a2bf9a0d2dfc3..1ac811f5db085 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -384,12 +384,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 932b3d95c276c..00e8ae6aa06da 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -208,8 +208,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) @@ -227,7 +226,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 } @@ -1481,19 +1483,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(diagnostics::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( - diagnostics::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 a4f80697fc93a9fc1999d769cb2eefcc9e6d9791 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:59:12 +0000 Subject: [PATCH 5/8] [PAC] 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_gcc/src/common.rs | 4 +- compiler/rustc_codegen_llvm/src/asm.rs | 8 +- compiler/rustc_codegen_llvm/src/common.rs | 20 ++- compiler/rustc_codegen_llvm/src/consts.rs | 169 +++++++++++++++++- .../rustc_codegen_ssa/src/traits/consts.rs | 5 +- 5 files changed, 194 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 712a1c14ef8e6..80396e7eb70de 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,9 +1,10 @@ use gccjit::{GlobalKind, LValue, RValue, ToRValue, Type}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, HasDataLayout}; +use rustc_abi::{self as abi, HasDataLayout, Size}; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, }; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::Mutability; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; use rustc_middle::ty::layout::LayoutOf; @@ -324,6 +325,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { layout: abi::Scalar, ty: Type<'gcc>, _ptrauth_schema: Option, + _ptrauth_discriminators: Option<&FxHashMap>, ) -> RValue<'gcc> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index a2cc114ba0318..d29035ba47632 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -166,7 +166,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ConstScalar::Ptr(ptr, _) => { let (prov, _) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); - let value = self.cx.alloc_to_backend(global_alloc, false, None).unwrap(); + let value = + self.cx.alloc_to_backend(global_alloc, false, None, None).unwrap(); inputs.push(value); op_idx.insert(idx, constraints.len()); constraints.push("s".to_string()); @@ -456,8 +457,9 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { ConstScalar::Ptr(ptr, _) => { let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); - let llval = - self.alloc_to_backend(global_alloc, true, None).unwrap(); + let llval = self + .alloc_to_backend(global_alloc, true, None, None) + .unwrap(); self.add_compiler_used_global(llval); let symbol = llvm::build_string(|s| unsafe { diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index c5c943002dfdc..7f4a49dcef818 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -4,11 +4,12 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _}; +use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _, Size}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_crate_store::DllImport; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_hashes::Hash128; use rustc_hir::def::DefKind; @@ -183,6 +184,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { global_alloc: GlobalAlloc<'tcx>, need_symbol_name: bool, ptrauth_schema: Option, + ptrauth_discriminators: Option<&FxHashMap>, ) -> Result<&'ll Value, u64> { let alloc = match global_alloc { GlobalAlloc::Function { instance, .. } => { @@ -229,7 +231,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } }; - let init = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ptrauth_discriminators, + ); let alloc = alloc.inner(); if need_symbol_name { @@ -409,6 +417,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { layout: abi::Scalar, llty: &'ll Type, ptrauth_schema: Option, + ptrauth_discriminators: Option<&FxHashMap>, ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { @@ -425,7 +434,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); let base_addr_space = global_alloc.address_space(self); - let base_addr = match self.alloc_to_backend(global_alloc, false, ptrauth_schema) { + let base_addr = match self.alloc_to_backend( + global_alloc, + false, + ptrauth_schema, + ptrauth_discriminators, + ) { Ok(base_addr) => base_addr, Err(base_addr) => { let val = base_addr.wrapping_add(offset.bytes()); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index bbb1141a92e89..93344b8160671 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::attrs::Linkage; use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; @@ -13,8 +14,9 @@ use rustc_middle::mir::interpret::{ read_target_uint, }; use rustc_middle::mono::MonoItem; +use rustc_middle::ptrauth::ptrauth_compute_fn_ptr_type_discriminator_for; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{self, Instance}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_span::{Symbol, bug, span_bug}; use rustc_target::spec::Arch; use tracing::{debug, instrument, trace}; @@ -36,11 +38,135 @@ pub(crate) enum IsInitOrFini { Yes, No, } + +/// 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: +/// - references +/// - direct function pointers +/// - 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>, +) -> FxHashMap { + let mut map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map); + + 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(disc) = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, ty) { + map.insert(base_offset, disc.into()); + + return; + } + + match ty.kind() { + ty::Ref(_, pointee, _) => { + collect_fn_ptr_discriminators_inner(tcx, typing_env, *pointee, 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, + ptrauth_discriminators: Option<&FxHashMap>, ) -> &'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 @@ -120,7 +246,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(), @@ -128,6 +254,16 @@ pub(crate) fn const_alloc_to_llvm<'ll>( } else { None }; + let discr = + ptrauth_discriminators.as_ref().and_then(|m| m.get(&Size::from_bytes(offset as u64))); + + // Init/fini entries must not participate in function pointer type discrimination, they use + // a dedicated constant value (ptrauth_string_discriminator("init_fini") which is: 0xd9d4). + 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 { @@ -136,6 +272,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( }, cx.type_ptr_ext(address_space), schema, + ptrauth_discriminators, )); next_offset = offset + pointer_size_bytes; } @@ -159,6 +296,15 @@ fn codegen_static_initializer<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { + let ptrauth_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 @@ -174,7 +320,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, + ptrauth_discriminators.as_ref(), + ), + alloc, + )) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -911,7 +1066,13 @@ 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); + // FIXME(jchlanda): Add support for pointer authentication type discrimination. + // `static_addr_of` only receives a `ConstAllocation`, so it does not have the type + // information needed to compute function pointer type discriminators. We'll likely need + // to either compute the discriminator map at callers that still know the Rust type, or + // extend this API to accept the required type information. See + // `codegen_static_initializer` for an example of how the discriminator map is computed. + 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 diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index b45b5667be6c9..7f82ebda2e5fb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -1,4 +1,6 @@ use rustc_abi as abi; +use rustc_abi::Size; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::interpret::Scalar; use rustc_session::PointerAuthSchema; @@ -40,7 +42,7 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option; fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value { - self.scalar_to_backend_with_pac(cv, layout, llty, None) + self.scalar_to_backend_with_pac(cv, layout, llty, None, None) } fn scalar_to_backend_with_pac( &self, @@ -48,6 +50,7 @@ pub trait ConstCodegenMethods: BackendTypes { layout: abi::Scalar, llty: Self::Type, ptrauth_schema: Option, + ptrauth_discriminators: Option<&FxHashMap>, ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; From 5ecec435dfd706d08931369faf1129dbb8f67162 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 29 Jul 2026 14:18:57 +0000 Subject: [PATCH 6/8] [PAC] Extend support to compile-time constants This covers standalone function pointer constants, promoted temporaries, immutable and mutable statics, arrays of function pointers, and mixed structs containing function pointers. Consult pauth-fn-ptr-type-discrimination-static-allocs.rs test for example uses. Revolves around threading PAC information through: * static_addr_of (StaticCodegenMethods) * from_const and from_const_alloc (both on rustc_codegen_ssa::mir::operand / OperandRef) --- compiler/rustc_codegen_gcc/src/common.rs | 2 +- compiler/rustc_codegen_gcc/src/consts.rs | 8 +- compiler/rustc_codegen_llvm/src/consts.rs | 147 ++-------------- compiler/rustc_codegen_ssa/src/meth.rs | 5 +- compiler/rustc_codegen_ssa/src/mir/operand.rs | 43 ++++- compiler/rustc_codegen_ssa/src/mir/retag.rs | 4 +- .../rustc_codegen_ssa/src/traits/statics.rs | 10 +- .../rustc_middle/src/ptrauth/discriminator.rs | 160 ++++++++++++++++-- compiler/rustc_middle/src/ptrauth/mod.rs | 2 +- compiler/rustc_middle/src/ty/vtable.rs | 23 +++ 10 files changed, 254 insertions(+), 150 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 80396e7eb70de..0b8b046f49c7d 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -116,7 +116,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { Mutability::Mut => { self.static_addr_of_mut(const_alloc_to_gcc(self, alloc), alloc.inner().align, None) } - _ => self.static_addr_of(alloc, None), + _ => self.static_addr_of(alloc, None, None), }; if !self.sess().fewer_names() { // FIXME(antoyo): set value name. diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 6c3b404547cfc..e05485ba04948 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -5,6 +5,7 @@ use rustc_abi::{self as abi, Align, HasDataLayout, Primitive, Size, WrappingRang use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::LOCAL_CRATE; @@ -56,7 +57,12 @@ fn set_global_alignment<'gcc, 'tcx>( } impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { - fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> RValue<'gcc> { + fn static_addr_of( + &self, + alloc: ConstAllocation<'_>, + kind: Option<&str>, + _ptrauth_discriminators: Option<&FxHashMap>, + ) -> RValue<'gcc> { let cv = const_alloc_to_gcc(self, alloc); let align = alloc.inner().align; diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 93344b8160671..1aa0ac6cbf22a 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::interpret::{ read_target_uint, }; use rustc_middle::mono::MonoItem; -use rustc_middle::ptrauth::ptrauth_compute_fn_ptr_type_discriminator_for; +use rustc_middle::ptrauth::ptrauth_collect_fn_ptr_discriminators; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_span::{Symbol, bug, span_bug}; @@ -39,128 +39,6 @@ pub(crate) enum IsInitOrFini { No, } -/// 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: -/// - references -/// - direct function pointers -/// - 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>, -) -> FxHashMap { - let mut map = FxHashMap::default(); - - collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map); - - 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(disc) = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, ty) { - map.insert(base_offset, disc.into()); - - return; - } - - match ty.kind() { - ty::Ref(_, pointee, _) => { - collect_fn_ptr_discriminators_inner(tcx, typing_env, *pointee, 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, @@ -300,7 +178,7 @@ fn codegen_static_initializer<'ll, 'tcx>( 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)) + Some(ptrauth_collect_fn_ptr_discriminators(cx.tcx, cx.typing_env(), ty)) } else { None }; @@ -1063,16 +941,21 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { /// /// The pointer will always be in the default address space. If global variables default to a /// different address space, an addrspacecast is inserted. - fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { + fn static_addr_of( + &self, + alloc: ConstAllocation<'_>, + kind: Option<&str>, + ptrauth_discriminators: Option<&FxHashMap>, + ) -> &'ll Value { // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the // same `ConstAllocation`? - // FIXME(jchlanda): Add support for pointer authentication type discrimination. - // `static_addr_of` only receives a `ConstAllocation`, so it does not have the type - // information needed to compute function pointer type discriminators. We'll likely need - // to either compute the discriminator map at callers that still know the Rust type, or - // extend this API to accept the required type information. See - // `codegen_static_initializer` for an example of how the discriminator map is computed. - let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No, None); + let cv = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ptrauth_discriminators, + ); 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 diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 9c024cacc35c9..b88a1b1c147f1 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -114,7 +114,10 @@ pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref)); let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory(); - let vtable = cx.static_addr_of(vtable_allocation, Some("vtable")); + // Vtables cannot contain extern "C"/"System" function pointers (asserted in + // vtable_allocation_provider), so they do not require function pointer type + // discriminators. + let vtable = cx.static_addr_of(vtable_allocation, Some("vtable"), None); cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); cx.create_vtable_debuginfo(ty, trait_ref, vtable); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 5b07eb84f40d7..50c196830d836 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -8,6 +8,7 @@ use rustc_abi::{ use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range}; use rustc_middle::mir::{self, ConstValue}; +use rustc_middle::ptrauth::ptrauth_collect_fn_ptr_discriminators; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; use rustc_session::config::{AnnotateMoves, DebugInfo, OptLevel}; @@ -182,7 +183,34 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let BackendRepr::Scalar(scalar) = layout.backend_repr else { bug!("from_const: invalid ByVal layout: {:#?}", layout); }; - let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout)); + let llval = match x { + Scalar::Ptr(..) => { + let pointee_ty = match ty.kind() { + ty::Ref(_, pointee, _) => *pointee, + _ => ty, + }; + let ptrauth_discriminators = + if bx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + Some(ptrauth_collect_fn_ptr_discriminators( + bx.tcx(), + bx.typing_env(), + pointee_ty, + )) + } else { + None + }; + let ptrauth_schema = bx.sess().pointer_authentication_functions(); + bx.scalar_to_backend_with_pac( + x, + scalar, + bx.immediate_backend_type(layout), + ptrauth_schema, + ptrauth_discriminators.as_ref(), + ) + } + _ => bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout)), + }; + OperandValue::Immediate(llval) } ConstValue::ZeroSized => return OperandRef::zero_sized(layout), @@ -267,8 +295,19 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } _ if layout.is_zst() => OperandRef::zero_sized(layout), _ => { + let ptrauth_discriminators = + if bx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + Some(ptrauth_collect_fn_ptr_discriminators( + bx.tcx(), + bx.typing_env(), + layout.ty, + )) + } else { + None + }; + // Neither a scalar nor scalar pair. Load from a place - let base_addr = bx.static_addr_of(alloc, None); + let base_addr = bx.static_addr_of(alloc, None, ptrauth_discriminators.as_ref()); let llval = bx.const_ptr_byte_offset(base_addr, offset); bx.load_operand(PlaceRef::new_sized(llval, layout)) diff --git a/compiler/rustc_codegen_ssa/src/mir/retag.rs b/compiler/rustc_codegen_ssa/src/mir/retag.rs index a71a57f02c82a..7652a656db8a3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/retag.rs +++ b/compiler/rustc_codegen_ssa/src/mir/retag.rs @@ -271,7 +271,9 @@ impl<'a, 'tcx, V> RetagPlan { let global_alloc = tcx.global_alloc(alloc_id); let global_mem = global_alloc.unwrap_memory(); - bx.cx().static_addr_of(global_mem, None) + // The range table contains only integer data, not pointer relocations, so no ptrauth + // discriminators are needed. + bx.cx().static_addr_of(global_mem, None, None) } } diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index c726213025350..20394c98812c1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -1,10 +1,18 @@ +use rustc_abi::Size; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_middle::mir::interpret::ConstAllocation; use super::BackendTypes; pub trait StaticCodegenMethods: BackendTypes { - fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> Self::Value; + fn static_addr_of( + &self, + alloc: ConstAllocation<'_>, + kind: Option<&str>, + ptrauth_discriminators: Option<&FxHashMap>, + ) -> Self::Value; + fn codegen_static(&mut self, def_id: DefId); } diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index 30226b8eebe07..65310ea0de427 100644 --- a/compiler/rustc_middle/src/ptrauth/discriminator.rs +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -1,11 +1,13 @@ //! 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. +//! discriminator computation used in pointer authentication, as well as the +//! machinery required to locate function pointer fields in Rust layouts that +//! require such discriminators. //! //! 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 +//! 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 @@ -15,14 +17,16 @@ //! //! ## Overview //! -//! The computation is structured into three conceptual stages: +//! The implementation 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. +//! //! One notable exception is C `_Complex`. Rust has no corresponding native type, //! so there is no canonical Rust representation to map onto Clang's `_Complex` //! type category. Rather than infer one (for example, by treating `(f32, f32)` @@ -31,29 +35,40 @@ //! encoding. //! //! ### 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`. //! +//! In addition to computing discriminators for individual function pointer +//! types, this module can recursively walk Rust type layouts and produce a map +//! from byte offsets to discriminators for function pointer fields contained +//! within aggregates. +//! //! ## Module structure //! //! - High-level API //! - `FnPtrDiscriminatorSource` //! - `ptrauth_compute_fn_ptr_type_discriminator_for` //! - `ptrauth_clone_discriminated_schema_for` +//! - `ptrauth_collect_fn_ptr_discriminators` //! //! - Low-level API //! - `FnPtrTypeDiscriminatorInput` //! - `compute_fn_ptr_type_discriminator` //! +//! - Layout traversal +//! - `ptrauth_collect_fn_ptr_discriminators` +//! //! - Signature extraction //! - `extract_fn_ptr_type` //! @@ -68,14 +83,16 @@ //! //! ## 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. +//! 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. +//! This implementation intentionally approximates Clang's behavior for +//! `extern "C"` and `extern "System"` function types only. It does NOT attempt +//! to model full Rust type system rules. -use rustc_abi::ExternAbi; +use rustc_abi::{ExternAbi, Size}; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, Unnormalized}; use rustc_session::PointerAuthSchema; use rustc_span::sym; @@ -256,6 +273,129 @@ fn extract_fn_ptr_type<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Option( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> FxHashMap { + let mut map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map); + + 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(disc) = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, ty) { + map.insert(base_offset, disc.into()); + + return; + } + + match ty.kind() { + ty::Ref(_, pointee, _) => { + collect_fn_ptr_discriminators_inner(tcx, typing_env, *pointee, 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); + } + } + } + _ => {} + } +} + /// Computes the Clang-compatible function pointer type discriminator. /// /// This is the low-level discriminator computation routine operating on an diff --git a/compiler/rustc_middle/src/ptrauth/mod.rs b/compiler/rustc_middle/src/ptrauth/mod.rs index 0eeef5e07f61c..46b3068524be7 100644 --- a/compiler/rustc_middle/src/ptrauth/mod.rs +++ b/compiler/rustc_middle/src/ptrauth/mod.rs @@ -3,5 +3,5 @@ pub mod llvm_siphash; pub use discriminator::{ FnPtrDiscriminatorSource, FnPtrTypeDiscriminatorInput, ptrauth_clone_discriminated_schema_for, - ptrauth_compute_fn_ptr_type_discriminator_for, + ptrauth_collect_fn_ptr_discriminators, ptrauth_compute_fn_ptr_type_discriminator_for, }; diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index fb56bda7d4562..95176361eac3e 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -85,6 +85,27 @@ pub(super) fn vtable_allocation_provider<'tcx>( tcx: TyCtxt<'tcx>, key: (Ty<'tcx>, Option>), ) -> AllocId { + let ptrauth_assert_not_c_abi_fn_ptr = |instance: ty::Instance<'tcx>| { + if tcx.sess.pointer_authentication_fn_ptr_type_discrimination() { + let sig = tcx + .instantiate_and_normalize_erasing_regions( + instance.args, + ty::TypingEnv::fully_monomorphized(), + tcx.fn_sig(instance.def_id()), + ) + .skip_binder(); + + assert!( + !matches!( + sig.abi(), + rustc_abi::ExternAbi::C { .. } | rustc_abi::ExternAbi::System { .. } + ), + "vtable entry unexpectedly has a C ABI function pointer type: {:?}", + instance + ); + } + }; + let (ty, poly_trait_ref) = key; let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { @@ -123,6 +144,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( VtblEntry::MetadataDropInPlace => { if ty.needs_drop(tcx, ty::TypingEnv::fully_monomorphized()) { let instance = ty::Instance::resolve_drop_glue(tcx, ty); + ptrauth_assert_not_c_abi_fn_ptr(instance); let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT); let fn_ptr = Pointer::from(fn_alloc_id); Scalar::from_pointer(fn_ptr, &tcx) @@ -134,6 +156,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size), VtblEntry::Vacant => continue, VtblEntry::Method(instance) => { + ptrauth_assert_not_c_abi_fn_ptr(instance); // Prepare the fn ptr we write into the vtable. let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance, CTFE_ALLOC_SALT); let fn_ptr = Pointer::from(fn_alloc_id); From bae13a97aab2e06e5e193061cbab75ef759ce2c8 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 10 Sep 2026 14:02:00 +0000 Subject: [PATCH 7/8] [PAC] Use scalar_to_backend_with_pac for const globals Also a fix for non function (closure, coroutines, etc) in discriminator_input. Fix in v-table assert. --- compiler/rustc_codegen_llvm/src/consts.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/operand.rs | 63 +++++++++++++------ .../rustc_middle/src/ptrauth/discriminator.rs | 32 +++++++--- compiler/rustc_middle/src/ty/vtable.rs | 15 ++--- 4 files changed, 73 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 1aa0ac6cbf22a..7d55696bb4bf1 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -16,7 +16,7 @@ use rustc_middle::mir::interpret::{ use rustc_middle::mono::MonoItem; use rustc_middle::ptrauth::ptrauth_collect_fn_ptr_discriminators; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance}; use rustc_span::{Symbol, bug, span_bug}; use rustc_target::spec::Arch; use tracing::{debug, instrument, trace}; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 50c196830d836..290aca1d355af 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -199,7 +199,13 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } else { None }; - let ptrauth_schema = bx.sess().pointer_authentication_functions(); + let mut ptrauth_schema = bx.sess().pointer_authentication_functions(); + if let Some(schema) = ptrauth_schema.as_mut() + && let Some(discr) = + ptrauth_discriminators.as_ref().and_then(|m| m.get(&Size::ZERO)) + { + schema.constant_discriminator = *discr as u16; + } bx.scalar_to_backend_with_pac( x, scalar, @@ -247,13 +253,44 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let alloc_align = alloc.inner().align; assert!(alloc_align >= layout.align.abi, "{alloc_align:?} < {:?}", layout.align.abi); - let read_scalar = |start, size, s: abi::Scalar, ty| { + let ptrauth_discriminators = + if bx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + Some(ptrauth_collect_fn_ptr_discriminators(bx.tcx(), bx.typing_env(), layout.ty)) + } else { + None + }; + + // `start` is the absolute position to read from within `alloc` (which may be a larger, + // shared allocation). `local_offset` is this field's offset within the value's own layout, + // used only to key into `ptrauth_discriminators`. The two are NOT interchangeable whenever + // `offset != 0` (e.g. the `b` field of a ScalarPair, or a value that isn't at the start of + // its allocation). + let read_scalar = |start: Size, local_offset: Size, size, s: abi::Scalar, ty| { match alloc.0.read_scalar( bx, alloc_range(start, size), /*read_provenance*/ matches!(s.primitive(), abi::Primitive::Pointer(_)), ) { - Ok(val) => bx.scalar_to_backend(val, s, ty), + Ok(val) => { + if let abi::Primitive::Pointer(_) = s.primitive() { + let mut schema = bx.sess().pointer_authentication_functions(); + if let Some(schema) = schema.as_mut() + && let Some(discr) = + ptrauth_discriminators.as_ref().and_then(|m| m.get(&local_offset)) + { + schema.constant_discriminator = *discr as u16; + } + bx.scalar_to_backend_with_pac( + val, + s, + ty, + schema, + ptrauth_discriminators.as_ref(), + ) + } else { + bx.scalar_to_backend(val, s, ty) + } + } Err(_) => bx.const_poison(ty), } }; @@ -268,7 +305,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { BackendRepr::Scalar(s @ abi::Scalar::Initialized { .. }) => { let size = s.size(bx); assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); - let val = read_scalar(offset, size, s, bx.immediate_backend_type(layout)); + let val = + read_scalar(offset, Size::ZERO, size, s, bx.immediate_backend_type(layout)); OperandRef { val: OperandValue::Immediate(val), layout, move_annotation: None } } BackendRepr::ScalarPair { @@ -281,12 +319,14 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { assert!(alloc_b_offset.bytes() > 0); let a_val = read_scalar( offset, + Size::ZERO, a_size, a, bx.scalar_pair_element_backend_type(layout, 0, true), ); let b_val = read_scalar( alloc_b_offset, + local_b_offset, b_size, b, bx.scalar_pair_element_backend_type(layout, 1, true), @@ -295,26 +335,13 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } _ if layout.is_zst() => OperandRef::zero_sized(layout), _ => { - let ptrauth_discriminators = - if bx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { - Some(ptrauth_collect_fn_ptr_discriminators( - bx.tcx(), - bx.typing_env(), - layout.ty, - )) - } else { - None - }; - - // Neither a scalar nor scalar pair. Load from a place + // Neither a scalar nor scalar pair. Load from a place. let base_addr = bx.static_addr_of(alloc, None, ptrauth_discriminators.as_ref()); - let llval = bx.const_ptr_byte_offset(base_addr, offset); bx.load_operand(PlaceRef::new_sized(llval, layout)) } } } - /// Asserts that this operand refers to a scalar and returns /// a reference to its value. pub fn immediate(self) -> V { diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index 65310ea0de427..d94d6ce644d51 100644 --- a/compiler/rustc_middle/src/ptrauth/discriminator.rs +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -63,7 +63,8 @@ //! - `ptrauth_collect_fn_ptr_discriminators` //! //! - Low-level API -//! - `FnPtrTypeDiscriminatorInput` +//! - `FnPtrTypeDiscriminatorInput` - canonical function signature input for +//! discriminator computation; exposes the function ABI through `abi()`. //! - `compute_fn_ptr_type_discriminator` //! //! - Layout traversal @@ -149,15 +150,22 @@ impl<'tcx> FnPtrDiscriminatorSource<'tcx> for Ty<'tcx> { /// normalized before constructing the canonical discriminator input. impl<'tcx> FnPtrDiscriminatorSource<'tcx> for Instance<'tcx> { fn discriminator_input(self, tcx: TyCtxt<'tcx>) -> Option> { - let sig = tcx - .instantiate_and_normalize_erasing_regions( - self.args, - ty::TypingEnv::fully_monomorphized(), - tcx.fn_sig(self.def_id()), - ) - .skip_binder(); - - Some(FnPtrTypeDiscriminatorInput::from_sig(sig)) + let typing_env = ty::TypingEnv::fully_monomorphized(); + + match self.ty(tcx, typing_env).kind() { + ty::FnDef(def_id, args) => { + let sig = tcx + .instantiate_and_normalize_erasing_regions( + args.skip_binder(), + typing_env, + tcx.fn_sig(*def_id), + ) + .skip_binder(); + Some(FnPtrTypeDiscriminatorInput::from_sig(sig)) + } + // Closures, coroutines, etc. are never called via an `extern "C"` function pointer. + _ => None, + } } } /// Enables discriminator computation directly from instantiated function @@ -231,6 +239,10 @@ pub struct FnPtrTypeDiscriminatorInput<'tcx> { } impl<'tcx> FnPtrTypeDiscriminatorInput<'tcx> { + pub fn abi(&self) -> ExternAbi { + self.abi + } + fn from_sig(sig: ty::FnSig<'tcx>) -> Self { FnPtrTypeDiscriminatorInput { inputs: sig.inputs(), diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 95176361eac3e..b5ae8e7292a5b 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -7,6 +7,7 @@ use rustc_type_ir::elaborate; use crate::mir::interpret::{ AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range, }; +use crate::ptrauth::discriminator::FnPtrDiscriminatorSource; use crate::ty::{self, Instance, TraitRef, Ty, TyCtxt}; #[derive(Clone, Copy, PartialEq, StableHash)] @@ -86,18 +87,12 @@ pub(super) fn vtable_allocation_provider<'tcx>( key: (Ty<'tcx>, Option>), ) -> AllocId { let ptrauth_assert_not_c_abi_fn_ptr = |instance: ty::Instance<'tcx>| { - if tcx.sess.pointer_authentication_fn_ptr_type_discrimination() { - let sig = tcx - .instantiate_and_normalize_erasing_regions( - instance.args, - ty::TypingEnv::fully_monomorphized(), - tcx.fn_sig(instance.def_id()), - ) - .skip_binder(); - + if tcx.sess.pointer_authentication_fn_ptr_type_discrimination() + && let Some(input) = instance.discriminator_input(tcx) + { assert!( !matches!( - sig.abi(), + input.abi(), rustc_abi::ExternAbi::C { .. } | rustc_abi::ExternAbi::System { .. } ), "vtable entry unexpectedly has a C ABI function pointer type: {:?}", From 849915551d3b275fa50346759a4d67d87de0d144 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 13:20:29 +0000 Subject: [PATCH 8/8] [PAC] Function pointer type discrimination for transmutes Implement pointer authentication resigning for function pointer transmutes that differ in their discriminators. Resigning only happens for function pointers (their transparent wrappers and Option). Aggregates (even those containing function pointer members) are deliberately kept as opaque values with no resigning. --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 318 ++++++++++++++++++- 1 file changed, 306 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 130958f3705c9..b1209ba0a0bed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,9 +1,12 @@ use std::assert_matches; use itertools::Itertools as _; -use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; +use rustc_abi::{self as abi, BackendRepr, ExternAbi, FIRST_VARIANT}; use rustc_index::IndexVec; use rustc_middle::mir; +use rustc_middle::ptrauth::{ + ptrauth_clone_discriminated_schema_for, ptrauth_compute_fn_ptr_type_discriminator_for, +}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Mutability, Ty, TyCtxt}; @@ -18,6 +21,13 @@ 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> { fn try_codegen_const_aggregate_as_immediate( &mut self, @@ -92,6 +102,236 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { true } + /// Lowers a transmute of an SSA operand while preserving pointer authentication semantics. + /// When the source and destination are both, or transparently wrap, function pointer types + /// with different type discriminators, the resulting pointer is re-signed. + /// + /// Aggregates that contain a function pointer field are intentionally treated as an ordinary + /// opaque-values, for which no pointer resigning is performed. + fn ptrauth_codegen_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 src_semantic = self.ptrauth_canonicalize_fn_ptr_layout(operand.layout); + let dst_semantic = self.ptrauth_canonicalize_fn_ptr_layout(cast); + + // Neither side is a function pointer - early return. + if src_semantic.is_none() && dst_semantic.is_none() { + return val; + } + + let info = TransmuteInfo { + src_ty: src_semantic.map_or(operand.layout.ty, |(ty, _)| ty), + dst_ty: dst_semantic.map_or(cast.ty, |(ty, _)| ty), + }; + + let nullable = src_semantic.is_some_and(|(_, n)| n) || dst_semantic.is_some_and(|(_, n)| n); + + let ptr = if nullable { + self.ptrauth_resign_transmuted_nullable_fn_ptr(bx, ptr, info) + } else { + self.ptrauth_resign_transmuted_fn_ptr(bx, ptr, info) + }; + + OperandValue::Immediate(ptr) + } + + /// Applies pointer-authentication type discriminator correction for a function pointer value + /// being transmuted between two types. + /// + /// If the source and destination types have different type discriminator values, the pointer + /// must be resigned using `llvm.ptrauth.resign` intrinsic. + /// + /// A discriminator value of `0` is used to represent non-function-pointer + /// or "raw pointer" 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 type discriminator. + /// + /// `TransmuteInfo` carries the source and destination types used to compute type + /// discriminators. These may differ from the original operand layouts after transparent + /// wrapper or nullable normalization. + fn ptrauth_resign_transmuted_fn_ptr( + &mut self, + bx: &mut Bx, + val: Bx::Value, + info: TransmuteInfo<'tcx>, + ) -> Bx::Value { + // Resigning can only happen in the context of function pointer type discrimination. + assert!(self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination()); + + let tcx = bx.tcx(); + + let src_disc = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, info.src_ty).unwrap_or(0); + let dst_disc = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, info.dst_ty).unwrap_or(0); + + if src_disc == dst_disc { + return val; + } + + let key = self.cx.tcx().sess.pointer_authentication_fn_ptr_key().unwrap() as u32; + bx.ptrauth_resign(val, key, src_disc.into(), key, dst_disc.into()) + } + + /// Resigns a nullable function pointer. + /// + /// `llvm.ptrauth.resign` performs an authenticate-and-resign operation on an already signed + /// pointer. A null function pointer carries no authentication signature, so it must bypass the + /// intrinsic and remain null. + fn ptrauth_resign_transmuted_nullable_fn_ptr( + &mut self, + bx: &mut Bx, + ptr: Bx::Value, + info: TransmuteInfo<'tcx>, + ) -> Bx::Value { + let ptrtoint = bx.ptrtoint(ptr, bx.type_isize()); + + // Fast path for compile time known null value. + if bx.const_to_opt_u128(ptrtoint, false) == Some(0) { + return bx.const_null(bx.type_ptr()); + } + + let pointer_align = bx.tcx().data_layout.pointer_align().abi; + let pointer_size = bx.tcx().data_layout.pointer_size(); + let result = bx.alloca(pointer_size, pointer_align); + + let null_bb = bx.append_sibling_block("ptrauth.null"); + let resign_bb = bx.append_sibling_block("ptrauth.resign"); + let end_bb = bx.append_sibling_block("ptrauth.end"); + + let is_null = bx.icmp(IntPredicate::IntEQ, ptrtoint, bx.const_usize(0)); + bx.cond_br(is_null, null_bb, resign_bb); + + bx.switch_to_block(null_bb); + bx.store(bx.const_null(bx.type_ptr()), result, pointer_align); + bx.br(end_bb); + + bx.switch_to_block(resign_bb); + let resigned = self.ptrauth_resign_transmuted_fn_ptr(bx, ptr, info); + bx.store(resigned, result, pointer_align); + bx.br(end_bb); + + bx.switch_to_block(end_bb); + bx.load(bx.type_ptr(), result, pointer_align) + } + + /// Returns the underlying function pointer type represented by `layout`. + /// + /// Transparent wrappers and `Option` are peeled until either a function pointer is reached + /// or a non-wrapper type is encountered. + /// + /// The returned boolean indicates whether an `Option` wrapper was seen, meaning the + /// function pointer uses a nullable representation. + fn ptrauth_canonicalize_fn_ptr_layout( + &self, + mut layout: TyAndLayout<'tcx>, + ) -> Option<(Ty<'tcx>, bool)> { + let mut nullable = false; + loop { + match layout.ty.kind() { + ty::FnPtr(..) | ty::FnDef(..) => { + return Some((layout.ty, nullable)); + } + + ty::Adt(def, _) if def.repr().transparent() => { + let Some((_, field_layout)) = layout.non_1zst_field(self.cx) else { + // Every field is a ZST - early exit. + return None; + }; + layout = field_layout; + } + + ty::Adt(def, args) + if self.cx.tcx().lang_items().option_type() == Some(def.did()) + // Only nullable-peel if `Option` genuinely niche-optimized to a single + // scalar, rejecting `Option>`. + && matches!(layout.backend_repr, abi::BackendRepr::Scalar(_)) => + { + nullable = true; + layout = self.cx.layout_of(args.type_at(0)); + } + + _ => return None, + } + } + } + + /// Applies pointer-authentication resign for a function pointer transmute and stores the + /// resigned value into the destination place. + /// + /// The source operand may be an SSA immediate, or may already reside in memory (its caller in + /// `ptrauth_codegen_transmute_place` receives the source as a place, not a register). + fn ptrauth_resign_fn_ptr( + &mut self, + bx: &mut Bx, + src: OperandRef<'tcx, Bx::Value>, + dst: PlaceRef<'tcx, Bx::Value>, + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx>, + nullable: bool, + ) { + let val = match src.val { + OperandValue::Immediate(v) => v, + + OperandValue::Ref(place) => bx.load_operand(place.with_type(src.layout)).immediate(), + + _ => { + bug!( + "unexpected operand representation for function pointer transmute: {:?}", + src.val + ); + } + }; + + let info = TransmuteInfo { src_ty, dst_ty }; + + let val = if nullable { + self.ptrauth_resign_transmuted_nullable_fn_ptr(bx, val, info) + } else { + self.ptrauth_resign_transmuted_fn_ptr(bx, val, info) + }; + + OperandRef { val: OperandValue::Immediate(val), layout: dst.layout, move_annotation: None } + .store_with_annotation(bx, dst); + } + + /// Applies pointer-authentication type discriminator correction when transmuting into a + /// memory-backed place. + /// + /// Resigning only happens when the transmute is directly between (or through a transparent + /// wrapper or `Option` around) extern "C" function pointer types. A transmute of an aggregate + /// that contains a function pointer field is deliberately lowered as an ordinary copy, with + /// no resigning. This matches how Clang lowers the analogous C code: a struct-level pointer + /// cast or struct copy does not re-sign a nested function-pointer field, and a real type + /// mismatch is instead caught by authentication failing at the point the pointer is actually + /// called. + fn ptrauth_codegen_transmute_place( + &mut self, + bx: &mut Bx, + src: OperandRef<'tcx, Bx::Value>, + dst: PlaceRef<'tcx, Bx::Value>, + ) { + if let (Some((src_ty, src_nullable)), Some((dst_ty, dst_nullable))) = ( + self.ptrauth_canonicalize_fn_ptr_layout(src.layout), + self.ptrauth_canonicalize_fn_ptr_layout(dst.layout), + ) { + self.ptrauth_resign_fn_ptr(bx, src, dst, src_ty, dst_ty, src_nullable || dst_nullable); + return; + } + + src.store_with_annotation(bx, dst.val.with_type(src.layout)); + } + fn is_entirely_uninit_const(&self, operand: &mir::Operand<'tcx>) -> bool { let mir::Operand::Constant(const_op) = operand else { return false }; self.eval_mir_constant(const_op).all_bytes_uninit(self.cx.tcx()) @@ -185,8 +425,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.ptrauth_codegen_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); } @@ -323,7 +580,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.ptrauth_codegen_transmute_place(bx, src, dst); + } else { + src.store_with_annotation(bx, dst.val.with_type(src.layout)); + } } } @@ -337,6 +599,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() { @@ -522,12 +794,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args.no_bound_vars().unwrap(), ) .unwrap(); - OperandValue::Immediate( - bx.get_fn_addr( - instance, - bx.sess().pointer_authentication_functions(), - ), + + let schema = if bx.sess().pointer_authentication_fn_ptr_type_discrimination() { + ptrauth_clone_discriminated_schema_for( + bx.tcx(), + bx.sess().pointer_authentication_functions(), + operand.layout.ty, ) + } else { + bx.sess().pointer_authentication_functions().clone() + }; + + OperandValue::Immediate(bx.get_fn_addr(instance, schema)) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -541,10 +819,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ty::ClosureKind::FnOnce, ); + assert!( + !matches!( + bx.cx().tcx().fn_sig(instance.def_id()).skip_binder().abi(), + ExternAbi::C { .. } | ExternAbi::System { .. } + ) + ); 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(), + /* ptrauth_schema */ None, ), ) } @@ -621,7 +909,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } mir::CastKind::Transmute | mir::CastKind::BoxDerefTransmute | mir::CastKind::Subtype => { - self.codegen_transmute_operand(bx, operand, cast) + if self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + self.ptrauth_codegen_transmute_operand(bx, operand, cast) + } else { + self.codegen_transmute_operand(bx, operand, cast) + } } }; OperandRef { val, layout: cast, move_annotation: None } @@ -766,8 +1058,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()); + // needs_thread_local_shim implies Windows/MSVC, for which pointer + // authentication is not yet supported. + assert!(!self.cx.tcx().sess.pointer_authentication()); + let fn_ptr = bx.get_fn_addr(instance, /* ptrauth_schema */ 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() {