From 5f3c64fb41c1fd50d3a2cff6ffb6ebda85aff2ec Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:01:47 +0000 Subject: [PATCH 1/9] [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 | 28 ++++++++++++++++++- 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.sparc-linux.stderr | 1 + tests/ui/abi/c-zst.sparc-none.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 ++ 31 files changed, 164 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index a1eab8f448990..de221ee22f2a0 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1943,6 +1943,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 21d92c6cc2936..42b92f55e0e80 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -332,7 +332,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 5f342e8b2dc33..16b3e84bfef66 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -455,7 +455,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 de8762d1c06b4..cf8d292ed0a6c 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -400,6 +400,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 7d59beb958104..9dbbd589c8769 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1563,6 +1563,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, '_> { @@ -2204,11 +2228,13 @@ 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 discriminator = fn_abi?.ptrauth_discriminator; + Some(llvm::OperandBundleBox::new( "ptrauth", &[ self.const_u32(schema.key as u32), - self.const_u64(schema.constant_discriminator as u64), + self.const_u64(discriminator as u64), ], )) } diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index a5a4f0b3ddb25..a78631d12268e 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 1059de3c0aec0..8b75a86d62a44 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -960,7 +960,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 @@ -975,7 +975,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 203274ccbb9fb..54ca0a7555ae1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -690,4 +690,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 f068aaa584dce..1c39124b995e7 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, @@ -1268,12 +1271,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 e14e285fc1185..61cb8cd45815f 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -674,12 +674,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) @@ -687,6 +690,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() } } @@ -1015,6 +1019,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 0f7771cb78b0b..433bb5b55767b 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -6,6 +6,7 @@ use rustc_attr_ir::find_attr; use rustc_attr_ir::lang_items::LangItem; use rustc_hir as hir; 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, @@ -615,6 +616,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 2ed9ffdf791f6..3c4c88ba2b19f 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:67:1 | diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index 0c7a6b664526d..ec27f2e5a754e 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:67:1 | diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index 0c7a6b664526d..ec27f2e5a754e 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:67:1 | diff --git a/tests/ui/abi/c-zst.sparc-linux.stderr b/tests/ui/abi/c-zst.sparc-linux.stderr index 0c7a6b664526d..8f2ee16925640 100644 --- a/tests/ui/abi/c-zst.sparc-linux.stderr +++ b/tests/ui/abi/c-zst.sparc-linux.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_discriminator: None, } --> $DIR/c-zst.rs:67:1 | diff --git a/tests/ui/abi/c-zst.sparc-none.stderr b/tests/ui/abi/c-zst.sparc-none.stderr index 0c7a6b664526d..8f2ee16925640 100644 --- a/tests/ui/abi/c-zst.sparc-none.stderr +++ b/tests/ui/abi/c-zst.sparc-none.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_discriminator: None, } --> $DIR/c-zst.rs:67:1 | diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index 0c7a6b664526d..ec27f2e5a754e 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:67:1 | diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 2ed9ffdf791f6..3c4c88ba2b19f 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:67: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 0c7a6b664526d..ec27f2e5a754e 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:67:1 | diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index fd72327d42fd7..dec34f32ecef9 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 | @@ -279,6 +280,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:43:1 | @@ -350,6 +352,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:46:1 | @@ -428,6 +431,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -494,6 +498,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -573,6 +578,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -646,6 +652,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:68:1 | @@ -718,6 +725,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -784,6 +792,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:71:1 | @@ -856,6 +865,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -922,6 +932,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:75:1 | @@ -1016,6 +1027,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:58:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 15d76f6e32078..193943be455e2 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 | @@ -279,6 +280,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:43:1 | @@ -350,6 +352,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:46:1 | @@ -428,6 +431,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -494,6 +498,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -573,6 +578,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -646,6 +652,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:68:1 | @@ -718,6 +725,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -784,6 +792,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:71:1 | @@ -856,6 +865,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -922,6 +932,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:75:1 | @@ -1016,6 +1027,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:58:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 15d76f6e32078..193943be455e2 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 | @@ -279,6 +280,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:43:1 | @@ -350,6 +352,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:46:1 | @@ -428,6 +431,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -494,6 +498,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -573,6 +578,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -646,6 +652,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:68:1 | @@ -718,6 +725,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -784,6 +792,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:71:1 | @@ -856,6 +865,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -922,6 +932,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:75:1 | @@ -1016,6 +1027,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:58: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 76d07c553a1a0..4fcc8087c015f 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 59d4e18e4cd7c..f4f30fe0537d2 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 b7e29098d6fe6..c87d517f18eab 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 2da42a7ef371fc8a83f9dc23b95d0d2ba52620d0 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 20 Aug 2026 06:47:34 +0000 Subject: [PATCH 2/9] [PAC] Use Option in FnAbi's discriminator field --- compiler/rustc_codegen_gcc/src/int.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 5 +++- 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(+), 68 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index cf8d292ed0a6c..539d25cd0a095 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -400,7 +400,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 9dbbd589c8769..98ee5a6dbcdee 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2228,7 +2228,10 @@ 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 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 61cb8cd45815f..b34ffbd2d2ef5 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -675,7 +675,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. @@ -1019,6 +1019,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 433bb5b55767b..72a92c53c7cd9 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -617,9 +617,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 3c4c88ba2b19f..c9ad40983c331 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:67:1 | diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index ec27f2e5a754e..8f2ee16925640 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:67:1 | diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index ec27f2e5a754e..8f2ee16925640 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:67:1 | diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index ec27f2e5a754e..8f2ee16925640 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:67:1 | diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 3c4c88ba2b19f..c9ad40983c331 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:67: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 ec27f2e5a754e..8f2ee16925640 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:67:1 | diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index dec34f32ecef9..e326bda71b0e8 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 | @@ -280,7 +280,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:43:1 | @@ -352,7 +352,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:46:1 | @@ -431,7 +431,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: [ @@ -498,7 +498,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 | @@ -578,7 +578,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: [ @@ -652,7 +652,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:68:1 | @@ -725,7 +725,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: [ @@ -792,7 +792,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:71:1 | @@ -865,7 +865,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: [ @@ -932,7 +932,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:75:1 | @@ -1027,7 +1027,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:58:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 193943be455e2..351adbbc4d22d 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 | @@ -280,7 +280,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:43:1 | @@ -352,7 +352,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:46:1 | @@ -431,7 +431,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: [ @@ -498,7 +498,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 | @@ -578,7 +578,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: [ @@ -652,7 +652,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:68:1 | @@ -725,7 +725,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: [ @@ -792,7 +792,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:71:1 | @@ -865,7 +865,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: [ @@ -932,7 +932,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:75:1 | @@ -1027,7 +1027,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:58:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 193943be455e2..351adbbc4d22d 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 | @@ -280,7 +280,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:43:1 | @@ -352,7 +352,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:46:1 | @@ -431,7 +431,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: [ @@ -498,7 +498,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 | @@ -578,7 +578,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: [ @@ -652,7 +652,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:68:1 | @@ -725,7 +725,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: [ @@ -792,7 +792,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:71:1 | @@ -865,7 +865,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: [ @@ -932,7 +932,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:75:1 | @@ -1027,7 +1027,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:58: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 4fcc8087c015f..93546fb5e0f77 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 f4f30fe0537d2..f49852d7483ac 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 c87d517f18eab..130d31eb07768 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 0b9d8bab1fbfcee01ed7fb9f1205e4a8497fac5b Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 10 Sep 2026 13:33:36 +0000 Subject: [PATCH 3/9] [PAC] repr(transparent) and correct handling of Option in encoder Also tighten the handling of enums. --- compiler/rustc_codegen_llvm/src/builder.rs | 5 +- .../rustc_middle/src/ptrauth/discriminator.rs | 72 ++++++++++++------- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 98ee5a6dbcdee..39338e81d083c 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2235,10 +2235,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { Some(llvm::OperandBundleBox::new( "ptrauth", - &[ - self.const_u32(schema.key as u32), - self.const_u64(discriminator as u64), - ], + &[self.const_u32(schema.key as u32), self.const_u64(discriminator as u64)], )) } diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index d44ecafb775ef..0ff48ff3d54db 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. /// @@ -315,35 +316,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_from_non_1zst(&cx).ty; + } + } + + if ty == before { + return ty; + } + } } /// Lowers a Rust type into a Clang-compatible discriminator type. @@ -386,8 +398,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 afff16c85cd4a3396b1a30c86ce9319de0568d3f Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:16:40 +0000 Subject: [PATCH 4/9] [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 +++++-------------- tests/ui/abi/debug.generic.stderr | 1 + tests/ui/abi/debug.loongarch64.stderr | 1 + tests/ui/abi/debug.riscv64.stderr | 1 + ...on_not_supported_pointer_authentication.rs | 12 ----------- ...ot_supported_pointer_authentication.stderr | 4 ---- 7 files changed, 8 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 e673435dae396..46cece7075924 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -394,12 +394,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 1c39124b995e7..dc27c76046d16 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 } @@ -1574,19 +1576,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/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index e326bda71b0e8..e584ee584038d 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -199,6 +199,7 @@ error: fn_abi_of(test_unsized) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 351adbbc4d22d..3df3831c98ecc 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -199,6 +199,7 @@ error: fn_abi_of(test_unsized) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 351adbbc4d22d..3df3831c98ecc 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -199,6 +199,7 @@ error: fn_abi_of(test_unsized) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | 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 2244741858bd646a9b00a8770c1fb8a5f71c0ddf Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:59:12 +0000 Subject: [PATCH 5/9] [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 | 170 +++++++++++++++++- .../rustc_codegen_ssa/src/traits/consts.rs | 5 +- 5 files changed, 195 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 42b92f55e0e80..8c00f0f378850 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; @@ -333,6 +334,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 21888720524a5..21ca616c615ca 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 a78631d12268e..b20542840fef0 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 6513cc0542ea5..13702268e4ea6 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -5,6 +5,7 @@ use rustc_attr_ir::Linkage; use rustc_attr_ir::lang_items::LangItem; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; @@ -13,8 +14,10 @@ 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::consts::ConstExt; 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 +39,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 +247,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 +255,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 +273,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 +297,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 +321,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) { @@ -914,7 +1070,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 e68cf0a5e18ada550687507bbf6f4952ba47b95f Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 29 Jul 2026 14:18:57 +0000 Subject: [PATCH 6/9] [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 | 150 ++-------------- 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 | 161 ++++++++++++++++-- compiler/rustc_middle/src/ptrauth/mod.rs | 2 +- compiler/rustc_middle/src/ty/vtable.rs | 23 +++ 10 files changed, 256 insertions(+), 152 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 8c00f0f378850..2a89ee4d0d3a5 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -117,7 +117,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 0784064547e0f..eb0c571b3ab7c 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -8,6 +8,7 @@ use rustc_attr_ir::Linkage; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::LOCAL_CRATE; use rustc_log::tracing::trace; @@ -60,7 +61,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 13702268e4ea6..588e7d2dbe04c 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -14,10 +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::consts::ConstExt; +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}; @@ -40,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, @@ -301,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 }; @@ -1067,16 +944,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 e887572ccc197..14ddc0d9c35e3 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_attr_ir::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::consts::ConstExt; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Ty}; @@ -183,7 +184,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), @@ -268,8 +296,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 6a3316fa93992..f1b77f1421310 100644 --- a/compiler/rustc_codegen_ssa/src/mir/retag.rs +++ b/compiler/rustc_codegen_ssa/src/mir/retag.rs @@ -294,7 +294,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 0ff48ff3d54db..d0a72206ad63e 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,19 +83,22 @@ //! //! ## 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; use crate::ptrauth::llvm_siphash::llvm_pointer_auth_stable_siphash; +use crate::ty::consts::ConstExt; use crate::ty::layout::LayoutCx; /// Types that can serve as a source for function pointer type discrimination. @@ -256,6 +274,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 b266f9d3a71edeb74321fe45e1bf633af0ae3da6 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 10 Sep 2026 14:02:00 +0000 Subject: [PATCH 7/9] [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_ssa/src/mir/operand.rs | 63 +++++++++++++------ .../rustc_middle/src/ptrauth/discriminator.rs | 32 +++++++--- compiler/rustc_middle/src/ty/vtable.rs | 15 ++--- 3 files changed, 72 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 14ddc0d9c35e3..fd0a1424f788b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -200,7 +200,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, @@ -248,13 +254,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), } }; @@ -269,7 +306,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 { @@ -282,12 +320,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), @@ -296,26 +336,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 d0a72206ad63e..8fe8858bd0847 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 @@ -150,15 +151,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 @@ -232,6 +240,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 e4e958a622f46c7fa82940daee0892750b3aa7da Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 21 Sep 2026 10:18:24 +0000 Subject: [PATCH 8/9] [PAC] Make sure that the encoder honors ABI compatibility rules for fn Meaning if two types are ABI compatible they must have the same encoding and hash value. Provide a ui test which groups the function pointers by the ABI compatibility rules and enforces the rule. --- compiler/rustc_attr_ir/src/data_structures.rs | 9 + .../rustc_attr_ir/src/encode_cross_crate.rs | 2 + .../src/attributes/rustc_dump.rs | 52 +- compiler/rustc_attr_parsing/src/context.rs | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_interface/src/passes.rs | 3 +- .../rustc_middle/src/ptrauth/discriminator.rs | 131 +++-- compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_passes/src/lib.rs | 1 + compiler/rustc_passes/src/ptrauth_test.rs | 42 ++ compiler/rustc_span/src/symbol.rs | 3 + tests/ui/README.md | 4 + ...scriminator-abi-compat-encoder-and-hash.rs | 215 +++++++ ...minator-abi-compat-encoder-and-hash.stderr | 530 ++++++++++++++++++ 14 files changed, 952 insertions(+), 43 deletions(-) create mode 100644 compiler/rustc_passes/src/ptrauth_test.rs create mode 100644 tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.rs create mode 100644 tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.stderr diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index bca4d8fa7b5ff..aced14e9bb0d0 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -586,6 +586,12 @@ pub enum RustcDumpLayoutKind { Size, } +#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] +pub enum RustcDumpPtrauthDiscriminatorKind { + Encoding, + Hash, +} + #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] pub enum RustcMirKind { PrettyLiveLocals, @@ -1250,6 +1256,9 @@ pub enum AttributeKind { /// Represents the [`rustc_dump_object_lifetime_defaults`](./attribute.rustc_dump_object_lifetime_defaults.html) attribute. RustcDumpObjectLifetimeDefaults, + /// Represents the [`rustc_dump_ptrauth_discriminator`](./attribute.:rustc_dump_ptrauth_discriminator.html) attribute. + RustcDumpPtrauthDiscriminator(ThinVec), + /// Represents the [`rustc_dump_symbol_name`](./attribute.rustc_dump_symbol_name.html) attribute. RustcDumpSymbolName(Span), diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index b6733e8c1db54..fe4c2f2c5a4d2 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -140,6 +140,7 @@ impl AttributeKind { RustcDumpItemBounds => No, RustcDumpLayout(..) => No, RustcDumpObjectLifetimeDefaults => No, + RustcDumpPtrauthDiscriminator(..) => No, RustcDumpSymbolName(..) => Yes, RustcDumpUserArgs => No, RustcDumpVariances => No, @@ -354,6 +355,7 @@ impl AttributeKind { RustcDumpItemBounds => false, RustcDumpLayout(..) => false, RustcDumpObjectLifetimeDefaults => false, + RustcDumpPtrauthDiscriminator(..) => false, RustcDumpSymbolName(..) => false, RustcDumpUserArgs => false, RustcDumpVariances => false, diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 99c7e933b07b4..66eb1168aa035 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -1,5 +1,5 @@ use rustc_attr_ir::target::{AssocCtxt, MethodKind, Target}; -use rustc_attr_ir::{AttributeKind, RustcDumpLayoutKind}; +use rustc_attr_ir::{AttributeKind, RustcDumpLayoutKind, RustcDumpPtrauthDiscriminatorKind}; use rustc_feature::AttributeStability; use rustc_span::{Span, Symbol, sym}; @@ -178,6 +178,56 @@ impl CombineAttributeParser for RustcDumpLayoutParser { } } +pub(crate) struct RustcDumpPtrauthDiscriminatorParser; + +impl CombineAttributeParser for RustcDumpPtrauthDiscriminatorParser { + const PATH: &[Symbol] = &[sym::rustc_dump_ptrauth_discriminator]; + + type Item = RustcDumpPtrauthDiscriminatorKind; + + const CONVERT: ConvertFn = + |items, _| AttributeKind::RustcDumpPtrauthDiscriminator(items); + + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); + + const TEMPLATE: AttributeTemplate = template!(List: &["encoding", "hash"]); + const STABILITY: AttributeStability = unstable!(rustc_attrs); + + fn extend( + cx: &mut AcceptContext<'_, '_>, + args: &ArgParser, + ) -> impl IntoIterator { + let Some(items) = cx.expect_list(args, cx.attr_span) else { + return vec![]; + }; + + let mut result = Vec::new(); + for item in items.mixed() { + let Some(arg) = item.meta_item_no_args() else { + cx.adcx().expected_not_literal(item.span()); + continue; + }; + let Some(ident) = arg.ident() else { + cx.adcx().expected_identifier(arg.span()); + return vec![]; + }; + let kind = match ident.name { + sym::ptrauth_encoding => RustcDumpPtrauthDiscriminatorKind::Encoding, + sym::ptrauth_hash => RustcDumpPtrauthDiscriminatorKind::Hash, + _ => { + cx.adcx().expected_specific_argument( + ident.span, + &[sym::ptrauth_encoding, sym::ptrauth_hash], + ); + continue; + } + }; + result.push(kind); + } + result + } +} + pub(crate) struct RustcDumpObjectLifetimeDefaultsParser; impl NoArgsAttributeParser for RustcDumpObjectLifetimeDefaultsParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 060fd74c1db21..50c92b740a4d9 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -194,6 +194,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, Combine, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index bc8c8f4b2a764..350ae8f802ba6 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -395,6 +395,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::rustc_dump_generics, sym::rustc_dump_hidden_type_of_opaques, sym::rustc_dump_layout, + sym::rustc_dump_ptrauth_discriminator, sym::rustc_abi, sym::rustc_regions, sym::rustc_delayed_bug_from_inside_query, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8975f01de5216..e53d805cde733 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -34,7 +34,7 @@ use rustc_middle::ty::{self, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; -use rustc_passes::{abi_test, input_stats, layout_test}; +use rustc_passes::{abi_test, input_stats, layout_test, ptrauth_test}; use rustc_resolve::{Resolver, ResolverOutputs}; use rustc_session::config::{Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::diagnostics::feature_err; @@ -1194,6 +1194,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { }); sess.time("layout_testing", || layout_test::test_layout(tcx)); + sess.time("ptrauth_testing", || ptrauth_test::test_ptrauth_discriminator(tcx)); sess.time("abi_testing", || abi_test::test_abi(tcx)); } diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index 8fe8858bd0847..9e903e4d28696 100644 --- a/compiler/rustc_middle/src/ptrauth/discriminator.rs +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -21,11 +21,12 @@ //! //! ### 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. +//! Rust types are first canonicalized according to ABI representation +//! (treating ABI-equivalent wrappers and niche representations as their +//! underlying representation, and treating pointer-like types uniformly), +//! then converted into a language-independent representation (`ClangDiscTy`) +//! that mirrors the type categories used by Clang when computing function +//! pointer discriminators. //! //! 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` @@ -92,11 +93,10 @@ //! `extern "C"` and `extern "System"` function types only. It does NOT attempt //! to model full Rust type system rules. -use rustc_abi::{ExternAbi, Size}; +use rustc_abi::{ExternAbi, Size, TagEncoding, Variants}; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, Unnormalized}; use rustc_session::PointerAuthSchema; -use rustc_span::sym; use crate::ptrauth::llvm_siphash::llvm_pointer_auth_stable_siphash; use crate::ty::consts::ConstExt; @@ -244,7 +244,7 @@ impl<'tcx> FnPtrTypeDiscriminatorInput<'tcx> { self.abi } - fn from_sig(sig: ty::FnSig<'tcx>) -> Self { + pub fn from_sig(sig: ty::FnSig<'tcx>) -> Self { FnPtrTypeDiscriminatorInput { inputs: sig.inputs(), output: sig.output(), @@ -409,16 +409,14 @@ fn collect_fn_ptr_discriminators_inner<'tcx>( } } -/// Computes the Clang-compatible function pointer type discriminator. -/// /// This is the low-level discriminator computation routine operating on an /// already constructed `FnPtrTypeDiscriminatorInput`. -fn compute_fn_ptr_type_discriminator<'tcx>( +fn encode_fn_ptr_type_discriminator<'tcx>( tcx: TyCtxt<'tcx>, input: &FnPtrTypeDiscriminatorInput<'tcx>, -) -> u16 { +) -> Option { if !matches!(input.abi, ExternAbi::C { .. } | ExternAbi::System { .. }) { - return 0; + return None; } let mut enc = PtrauthEncoder::new(); @@ -436,7 +434,30 @@ fn compute_fn_ptr_type_discriminator<'tcx>( enc.push(b'E'); - enc.finish() + Some(enc) +} + +/// Computes the Clang-compatible function pointer type discriminator (hashed). +/// +/// Returns `0` for function ABIs that are not supported by the Clang-compatible +/// encoding. +pub fn compute_fn_ptr_type_discriminator<'tcx>( + tcx: TyCtxt<'tcx>, + input: &FnPtrTypeDiscriminatorInput<'tcx>, +) -> u16 { + encode_fn_ptr_type_discriminator(tcx, input).map(|enc| enc.finish().into()).unwrap_or(0) +} + +/// Returns the raw Clang-compatible type encoding used as input to the +/// discriminator hash. +/// +/// This is primarily used for debugging and comparing Rust's encoding against +/// Clang's `encodeTypeForFunctionPointerAuth`. +pub fn debug_encode_fn_ptr_type<'tcx>( + tcx: TyCtxt<'tcx>, + input: &FnPtrTypeDiscriminatorInput<'tcx>, +) -> String { + encode_fn_ptr_type_discriminator(tcx, input).map(|enc| enc.debug_string()).unwrap_or_default() } // Clang disc type. @@ -452,6 +473,9 @@ enum ClangDiscTy<'tcx> { // - raw pointers (`*const T`, `*mut T`) // - Rust references (`&T`, `&mut T`) // - function pointers + // - `Box` (canonicalized to a raw pointer) + // - `NonNull` (canonicalized through its transparent representation) + // - DST pointer-like types (`dyn Trait`, slices, `str`) // All collapse to a single Clang-compatible 'P' node. Pointer, @@ -469,42 +493,62 @@ enum ClangDiscTy<'tcx> { Void, } -// 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 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`. -// -// 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> { +/// Canonicalizes Rust types that have the same ABI representation as a simpler +/// C-compatible type for discriminator purposes. +/// +/// This is intentionally representation-based rather than purely semantic: +/// types that have the same ABI representation are lowered to the same +/// discriminator type. This includes: +/// - pattern types, which are transparent with respect to representation; +/// - `Box`, which is represented as a thin pointer; +/// - size-0, alignment-1 types, which are represented like `()`; +/// - `repr(transparent)` wrappers, which are represented like their non-ZST +/// field; +/// - two-variant niche enums, which are represented like their payload field. +/// +/// The canonicalization is repeated until reaching a fixed point because one +/// transformation can expose another canonicalization opportunity. +fn canonicalize_abi_compatible_type<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> { + let typing_env = ty::TypingEnv::fully_monomorphized(); + 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; - } + if let ty::Pat(base, _) = ty.kind() { + ty = *base; } - // 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 ty.is_box_global(tcx) { + ty = Ty::new_imm_ptr( + tcx, + ty.boxed_ty().expect("is_box_global() returned true, so this must be a Box"), + ); + } - if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) { + if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) { + if layout.is_zst() && layout.align.abi.bytes() == 1 { + ty = tcx.types.unit; + } else if matches!(ty.kind(), ty::Adt(..)) { let cx = LayoutCx::new(tcx, typing_env); ty = layout.peel_transparent_wrappers_from_non_1zst(&cx).ty; } } + if let ty::Adt(def, args) = ty.kind() + && def.is_enum() + && def.variants().len() == 2 + && let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) + && let Variants::Multiple { + tag_encoding: TagEncoding::Niche { untagged_variant, .. }, + .. + } = layout.variants + { + let variant = def.variant(untagged_variant); + if let [field] = &variant.fields.raw[..] { + ty = tcx.normalize_erasing_regions(typing_env, field.ty(tcx, args)); + } + } + if ty == before { return ty; } @@ -531,7 +575,7 @@ fn canonicalize_c_type<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> { /// `_Complex` types. /// This must remain in sync with Clang's `encodeTypeForFunctionPointerAuth`. fn to_clang_disc_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ClangDiscTy<'tcx> { - let ty = canonicalize_c_type(tcx, ty); + let ty = canonicalize_abi_compatible_type(tcx, ty); match ty.kind() { // C void / Rust () _ if ty.is_unit() => ClangDiscTy::Void, @@ -543,7 +587,8 @@ fn to_clang_disc_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ClangDiscTy<'tcx> ty::Int(_) | ty::Uint(_) => ClangDiscTy::Int, ty::Float(f) => ClangDiscTy::Float(f), - // everything pointer-like collapses + // Pointer-like types form a discriminator boundary: the pointee type + // does not participate in the encoding. ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) | ty::Dynamic(..) | ty::Slice(_) | ty::Str => { ClangDiscTy::Pointer } @@ -609,6 +654,10 @@ impl PtrauthEncoder { fn finish(&self) -> u16 { llvm_pointer_auth_stable_siphash(&self.buf) } + + fn debug_string(&self) -> String { + String::from_utf8_lossy(&self.buf).into_owned() + } } /// Encodes a ClangDiscTy into the discriminator byte stream. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3eb6892177b05..798f4cd6a6b81 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -337,6 +337,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcDumpItemBounds => (), AttributeKind::RustcDumpLayout(..) => (), AttributeKind::RustcDumpObjectLifetimeDefaults => (), + AttributeKind::RustcDumpPtrauthDiscriminator(..) => (), AttributeKind::RustcDumpSymbolName(..) => (), AttributeKind::RustcDumpUserArgs => (), AttributeKind::RustcDumpVariances => (), diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 77bd179e8dd80..9c1f9c83e067d 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -28,6 +28,7 @@ pub mod input_stats; mod lang_items; pub mod layout_test; mod lib_features; +pub mod ptrauth_test; mod reachable; pub mod stability; mod upvars; diff --git a/compiler/rustc_passes/src/ptrauth_test.rs b/compiler/rustc_passes/src/ptrauth_test.rs new file mode 100644 index 0000000000000..668f57b148c19 --- /dev/null +++ b/compiler/rustc_passes/src/ptrauth_test.rs @@ -0,0 +1,42 @@ +use rustc_attr_ir::{RustcDumpPtrauthDiscriminatorKind, find_attr}; +use rustc_hir::def::DefKind; +use rustc_middle::ptrauth::FnPtrTypeDiscriminatorInput; +use rustc_middle::ptrauth::discriminator::{ + compute_fn_ptr_type_discriminator, debug_encode_fn_ptr_type, +}; +use rustc_middle::ty::TyCtxt; + +pub fn test_ptrauth_discriminator(tcx: TyCtxt<'_>) { + if !tcx.features().rustc_attrs() { + return; + } + for def_id in tcx.hir_crate_items(()).definitions() { + if tcx.def_kind(def_id) != DefKind::Fn { + continue; + } + let Some(kinds) = find_attr!(tcx, def_id, RustcDumpPtrauthDiscriminator(kinds) => kinds) + else { + continue; + }; + + let sig = tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + let input = FnPtrTypeDiscriminatorInput::from_sig(sig); + let span = tcx.def_span(def_id); + + for kind in kinds { + let message = match kind { + RustcDumpPtrauthDiscriminatorKind::Encoding => { + format!( + "ptrauth discriminator encoding: \"{}\"", + debug_encode_fn_ptr_type(tcx, &input) + ) + } + RustcDumpPtrauthDiscriminatorKind::Hash => { + let res = compute_fn_ptr_type_discriminator(tcx, &input); + format!("ptrauth discriminator hash: {} (0x{:x})", res, res) + } + }; + tcx.dcx().span_err(span, message); + } + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b36e13b66ba78..f767d3f106541 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1694,6 +1694,8 @@ symbols! { ptr_write_bytes, ptr_write_unaligned, ptr_write_volatile, + ptrauth_encoding, + ptrauth_hash, pub_macro_rules, pub_restricted, public, @@ -1850,6 +1852,7 @@ symbols! { rustc_dump_item_bounds, rustc_dump_layout, rustc_dump_object_lifetime_defaults, + rustc_dump_ptrauth_discriminator, rustc_dump_symbol_name, rustc_dump_user_args, rustc_dump_variances, diff --git a/tests/ui/README.md b/tests/ui/README.md index 27995e0ab4cd5..9570cdee8b33c 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1116,6 +1116,10 @@ Contains only 2 tests, related to a single issue, which was about an error cause **FIXME**: Probably rehome under some typecheck / binop directory. +## `tests/ui/ptrauth/`: Function pointer type discrimination + +Contain only 1 tests, related to how Rust computes function pointer type discriminators for pointer authenticated code. The test uses `ptrauth_encoding` and `ptrauth_hash` attributes to obtain string encoding and hashed value respectively. + ## `tests/ui/pub/`: `pub` keyword A large category about function and type public/private visibility, and its impact when using features across crates. Checks both visibility-related error messages and previously buggy cases. diff --git a/tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.rs b/tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.rs new file mode 100644 index 0000000000000..6c4b6225a775f --- /dev/null +++ b/tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.rs @@ -0,0 +1,215 @@ +// Exercises the `fn` ABI-compatibility guarantees documented at +// https://doc.rust-lang.org/std/primitive.fn.html#abi-compatibility +// in the context of function pointer type discrimination. +// +// Within each group, every function's parameter type must be ABI-compatible +// with every other's, and must produce an identical discriminator. `*_neg` +// functions are negative controls and must NOT match the group they sit beside. +// +// NOTE: The doc's fourth compatibility rule +// "Any two fn (function pointer) types are ABI-compatible with each other if +// they have the same ABI string or the ABI string only differs in a trailing +// -unwind, independent of the rest of their signature. (This means you can pass +// fn() to a function expecting fn(i32), and the call will be valid ABI-wise. +// The callee receives the result of transmuting the function pointer from fn() +// to fn(i32); that transmutation is itself a well-defined operation, it’s just +// almost certainly UB to later call that function pointer.)" is +// satisfied for function pointer types used as VALUES (e.g. a callback parameter). +// Those collapse to 'P' via the same blanket pointer-merge that handles +// every other pointer-like type. It is deliberately *NOT HONORED* for the top-level signature +// being authenticated here. +// +// Honoring the rule fully would mean that every extern "C"/"System" function +// produces the identical discriminator, making function pointer type +// discrimination useless. + +//@ dont-require-annotations: ERROR +#![crate_type = "lib"] +#![feature(rustc_attrs)] +#![allow(dead_code)] +#![allow(improper_ctypes_definitions)] +#![feature(allocator_ext)] + +use std::alloc::{AllocError, Allocator, Global, Layout}; +use std::marker::PhantomData; +use std::num::NonZero; +use std::ptr::NonNull; + +struct SomeStruct { + _x: i32, +} + +// "*const T, *mut T, &T, &mut T, Box (specifically, only Box), +// and NonNull are all ABI-compatible with each other for all T. They are +// also ABI-compatible with each other for different T if they have the same +// metadata type (::Metadata)." +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_a(_: *const i32) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_b(_: *mut i32) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_c(_: &i32) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_d(_: &mut i32) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_e(_: Box) {} // expect: "FvPE" 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_f(_: NonNull) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_g(_: *const SomeStruct) {} // expect: "FvPE" 10942 (0x2abe) + +struct MyAlloc; +unsafe impl Allocator for MyAlloc { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + Global.allocate(layout) + } + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + unsafe { Global.deallocate(ptr, layout) } + } +} +// negative test for "specifically, only Box" +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g1_box_alloc_neg(_: Box) {} // expect: "Fv3BoxE": 3916 (0xf4c) + +// "usize is ABI-compatible with the uN integer type of the same size, and +// likewise isize is ABI-compatible with the iN integer type of the same size. +// and +// "char is ABI-compatible with u32." +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g2_a(_: i32) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g2_b(_: u64) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g2_c(_: usize) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g2_d(_: isize) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g2_e(_: char) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g2_f(_: bool) {} // expect: "FviE": 2712 (0xa98) + +// "Any two types with size 0 and alignment 1 are ABI-compatible." +struct Unit; +enum OneVariant { + A, +} +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g3_a(_: ()) {} // expect: "FvvE": 61000 (0xee48) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g3_b(_: Unit) {} // expect: "FvvE": 61000 (0xee48) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g3_c(_: PhantomData) {} // expect: "FvvE": 61000 (0xee48) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g3_d(_: [u8; 0]) {} // expect: "FvvE": 61000 (0xee48) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g3_e(_: OneVariant) {} // expect: "FvvE": 61000 (0xee48) +// 1-ZST, not 4-ZST, must not match +#[repr(align(4))] +struct AlignedMarker; +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g3_neg(_: AlignedMarker) {} // expect: "Fv13AlignedMarkerE": 49590 (0xc1b6) + +// "A repr(transparent) type T is ABI-compatible with its unique non-trivial +// field, i.e., the unique field that doesn’t have size 0 and alignment 1 (if +// there is such a field)." +#[repr(transparent)] +struct Wrapper(i32); +#[repr(transparent)] +struct WrapperWithZst(i32, PhantomData); +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g4_a(_: i32) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g4_b(_: Wrapper) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g4_c(_: WrapperWithZst) {} // expect: "FviE": 2712 (0xa98) +// i32 wrapped in a non-transparent struct +struct NotTransparent(i32); +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g4_neg(_: NotTransparent) {} // expect: "Fv14NotTransparentE": 37756 (0x937c) + +// "i32 is ABI-compatible with NonZero, and similar for all other integer +// types." +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g5_a(_: i32) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g5_b(_: NonZero) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g5_c(_: i16) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g5_d(_: NonZero) {} // expect: "FviE": 2712 (0xa98) + +// If T is guaranteed to be subject to the null pointer optimization, and E is +// an enum satisfying the following requirements, then T and E are +// ABI-compatible. Such an enum E is called “option-like”. +// * The enum E uses the Rust representation, and is not modified by the align +// or packed representation modifiers. +// * The enum E has exactly two variants. +// * One variant has exactly one field, of type T. +// * All fields of the other variant are zero-sized with 1-byte alignment. +enum MyOption { + None, + Some(T), +} +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_ref_a(_: &i32) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_ref_b(_: Option<&i32>) {} // expect: "FvPE": 10942 (0x2abe) + +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_fn_a(_: fn()) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_fn_b(_: Option) {} // expect: "FvPE": 10942 (0x2abe) + +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_nn_a(_: NonNull) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_nn_b(_: Option>) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_nn_c(_: MyOption>) {} // expect: "FvPE": 10942 (0x2abe) + +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_box_a(_: Box) {} // expect: "FvPE": 10942 (0x2abe) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_box_b(_: Option>) {} // expect: "FvPE": 10942 (0x2abe) +// matches g2/g4/g5 ("FviE": 2712 (0xa98)), NOT g6_ref/fn/nn/box ("FvPE": 10942 +// (0x2abe)) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_nz_a(_: NonZero) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_nz_b(_: Option>) {} // expect: "FviE": 2712 (0xa98) + +// Option has no niche to exploit, NPO is not happening. +// Carries a real discriminant, must NOT match plain i32. +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_neg(_: Option) {} // expect: "Fv6OptionE": 20395 (0x4fab) + +// Niche field and an extra field. Must NOT fold to NonZero (the second +// field changes the ABI) +enum WithExtraField { + A(NonZero, i32), + B, +} +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_multifield_neg(_: WithExtraField) {} // expect: "Fv14WithExtraFieldE": 34575 (0x870f) + +// More than 2 variants sharing one niche field's spare values. +enum ThreeVariants { + A(bool), + B, + C, +} +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g6_arity_neg(_: ThreeVariants) {} // expect: "Fv13ThreeVariantsE": 9871 (0x268f) + // +// "Any two fn (function pointer) types are ABI-compatible with each other if +// they have the same ABI string or the ABI string only differs in a trailing +// -unwind, independent of the rest of their signature." +// +// This is only true for the first part of the rule, if signature differs, the +// discriminator *must* differ (unless it the function pointer is used as an +// argument to a different function, in which case it is encoded as any other +// pointer like type: `P`). +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn g7_a(_: i32) {} // expect: "FviE": 2712 (0xa98) +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C-unwind" fn g7_b(_: i32) {} // expect: "FviE": 2712 (0xa98) diff --git a/tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.stderr b/tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.stderr new file mode 100644 index 0000000000000..f5e84015d7799 --- /dev/null +++ b/tests/ui/ptrauth/discriminator-abi-compat-encoder-and-hash.stderr @@ -0,0 +1,530 @@ +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:47:1 + | +LL | extern "C" fn g1_a(_: *const i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:47:1 + | +LL | extern "C" fn g1_a(_: *const i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:49:1 + | +LL | extern "C" fn g1_b(_: *mut i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:49:1 + | +LL | extern "C" fn g1_b(_: *mut i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:51:1 + | +LL | extern "C" fn g1_c(_: &i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:51:1 + | +LL | extern "C" fn g1_c(_: &i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:53:1 + | +LL | extern "C" fn g1_d(_: &mut i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:53:1 + | +LL | extern "C" fn g1_d(_: &mut i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:55:1 + | +LL | extern "C" fn g1_e(_: Box) {} // expect: "FvPE" 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:55:1 + | +LL | extern "C" fn g1_e(_: Box) {} // expect: "FvPE" 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:57:1 + | +LL | extern "C" fn g1_f(_: NonNull) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:57:1 + | +LL | extern "C" fn g1_f(_: NonNull) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:59:1 + | +LL | extern "C" fn g1_g(_: *const SomeStruct) {} // expect: "FvPE" 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:59:1 + | +LL | extern "C" fn g1_g(_: *const SomeStruct) {} // expect: "FvPE" 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "Fv3BoxE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:72:1 + | +LL | extern "C" fn g1_box_alloc_neg(_: Box) {} // expect: "Fv3BoxE": 3916 (0xf4c) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 3916 (0xf4c) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:72:1 + | +LL | extern "C" fn g1_box_alloc_neg(_: Box) {} // expect: "Fv3BoxE": 3916 (0xf4c) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:79:1 + | +LL | extern "C" fn g2_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:79:1 + | +LL | extern "C" fn g2_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:81:1 + | +LL | extern "C" fn g2_b(_: u64) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:81:1 + | +LL | extern "C" fn g2_b(_: u64) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:83:1 + | +LL | extern "C" fn g2_c(_: usize) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:83:1 + | +LL | extern "C" fn g2_c(_: usize) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:85:1 + | +LL | extern "C" fn g2_d(_: isize) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:85:1 + | +LL | extern "C" fn g2_d(_: isize) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:87:1 + | +LL | extern "C" fn g2_e(_: char) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:87:1 + | +LL | extern "C" fn g2_e(_: char) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:89:1 + | +LL | extern "C" fn g2_f(_: bool) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:89:1 + | +LL | extern "C" fn g2_f(_: bool) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvvE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:97:1 + | +LL | extern "C" fn g3_a(_: ()) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 61000 (0xee48) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:97:1 + | +LL | extern "C" fn g3_a(_: ()) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvvE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:99:1 + | +LL | extern "C" fn g3_b(_: Unit) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 61000 (0xee48) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:99:1 + | +LL | extern "C" fn g3_b(_: Unit) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvvE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:101:1 + | +LL | extern "C" fn g3_c(_: PhantomData) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 61000 (0xee48) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:101:1 + | +LL | extern "C" fn g3_c(_: PhantomData) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvvE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:103:1 + | +LL | extern "C" fn g3_d(_: [u8; 0]) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 61000 (0xee48) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:103:1 + | +LL | extern "C" fn g3_d(_: [u8; 0]) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvvE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:105:1 + | +LL | extern "C" fn g3_e(_: OneVariant) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 61000 (0xee48) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:105:1 + | +LL | extern "C" fn g3_e(_: OneVariant) {} // expect: "FvvE": 61000 (0xee48) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "Fv13AlignedMarkerE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:110:1 + | +LL | extern "C" fn g3_neg(_: AlignedMarker) {} // expect: "Fv13AlignedMarkerE": 49590 (0xc1b6) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 49590 (0xc1b6) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:110:1 + | +LL | extern "C" fn g3_neg(_: AlignedMarker) {} // expect: "Fv13AlignedMarkerE": 49590 (0xc1b6) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:120:1 + | +LL | extern "C" fn g4_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:120:1 + | +LL | extern "C" fn g4_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:122:1 + | +LL | extern "C" fn g4_b(_: Wrapper) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:122:1 + | +LL | extern "C" fn g4_b(_: Wrapper) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:124:1 + | +LL | extern "C" fn g4_c(_: WrapperWithZst) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:124:1 + | +LL | extern "C" fn g4_c(_: WrapperWithZst) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "Fv14NotTransparentE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:128:1 + | +LL | extern "C" fn g4_neg(_: NotTransparent) {} // expect: "Fv14NotTransparentE": 37756 (0x937c) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 37756 (0x937c) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:128:1 + | +LL | extern "C" fn g4_neg(_: NotTransparent) {} // expect: "Fv14NotTransparentE": 37756 (0x937c) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:133:1 + | +LL | extern "C" fn g5_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:133:1 + | +LL | extern "C" fn g5_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:135:1 + | +LL | extern "C" fn g5_b(_: NonZero) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:135:1 + | +LL | extern "C" fn g5_b(_: NonZero) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:137:1 + | +LL | extern "C" fn g5_c(_: i16) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:137:1 + | +LL | extern "C" fn g5_c(_: i16) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:139:1 + | +LL | extern "C" fn g5_d(_: NonZero) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:139:1 + | +LL | extern "C" fn g5_d(_: NonZero) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:154:1 + | +LL | extern "C" fn g6_ref_a(_: &i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:154:1 + | +LL | extern "C" fn g6_ref_a(_: &i32) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:156:1 + | +LL | extern "C" fn g6_ref_b(_: Option<&i32>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:156:1 + | +LL | extern "C" fn g6_ref_b(_: Option<&i32>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:159:1 + | +LL | extern "C" fn g6_fn_a(_: fn()) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:159:1 + | +LL | extern "C" fn g6_fn_a(_: fn()) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:161:1 + | +LL | extern "C" fn g6_fn_b(_: Option) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:161:1 + | +LL | extern "C" fn g6_fn_b(_: Option) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:164:1 + | +LL | extern "C" fn g6_nn_a(_: NonNull) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:164:1 + | +LL | extern "C" fn g6_nn_a(_: NonNull) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:166:1 + | +LL | extern "C" fn g6_nn_b(_: Option>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:166:1 + | +LL | extern "C" fn g6_nn_b(_: Option>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:168:1 + | +LL | extern "C" fn g6_nn_c(_: MyOption>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:168:1 + | +LL | extern "C" fn g6_nn_c(_: MyOption>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:171:1 + | +LL | extern "C" fn g6_box_a(_: Box) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:171:1 + | +LL | extern "C" fn g6_box_a(_: Box) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FvPE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:173:1 + | +LL | extern "C" fn g6_box_b(_: Option>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 10942 (0x2abe) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:173:1 + | +LL | extern "C" fn g6_box_b(_: Option>) {} // expect: "FvPE": 10942 (0x2abe) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:177:1 + | +LL | extern "C" fn g6_nz_a(_: NonZero) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:177:1 + | +LL | extern "C" fn g6_nz_a(_: NonZero) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:179:1 + | +LL | extern "C" fn g6_nz_b(_: Option>) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:179:1 + | +LL | extern "C" fn g6_nz_b(_: Option>) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "Fv6OptionE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:184:1 + | +LL | extern "C" fn g6_neg(_: Option) {} // expect: "Fv6OptionE": 20395 (0x4fab) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 20395 (0x4fab) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:184:1 + | +LL | extern "C" fn g6_neg(_: Option) {} // expect: "Fv6OptionE": 20395 (0x4fab) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "Fv14WithExtraFieldE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:193:1 + | +LL | extern "C" fn g6_multifield_neg(_: WithExtraField) {} // expect: "Fv14WithExtraFieldE": 34575 (0x870f) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 34575 (0x870f) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:193:1 + | +LL | extern "C" fn g6_multifield_neg(_: WithExtraField) {} // expect: "Fv14WithExtraFieldE": 34575 (0x870f) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "Fv13ThreeVariantsE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:202:1 + | +LL | extern "C" fn g6_arity_neg(_: ThreeVariants) {} // expect: "Fv13ThreeVariantsE": 9871 (0x268f) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 9871 (0x268f) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:202:1 + | +LL | extern "C" fn g6_arity_neg(_: ThreeVariants) {} // expect: "Fv13ThreeVariantsE": 9871 (0x268f) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:213:1 + | +LL | extern "C" fn g7_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:213:1 + | +LL | extern "C" fn g7_a(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator encoding: "FviE" + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:215:1 + | +LL | extern "C-unwind" fn g7_b(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/discriminator-abi-compat-encoder-and-hash.rs:215:1 + | +LL | extern "C-unwind" fn g7_b(_: i32) {} // expect: "FviE": 2712 (0xa98) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 88 previous errors + From 051269bb424dabf4f1763599d1607a5153136110 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 5 Oct 2026 12:27:30 +0000 Subject: [PATCH 9/9] [PAC] Add `rustc_dump_ptrauth_discriminator` attribute doc And corresponding doc_example. --- compiler/rustc_attr_ir/src/attribute_docs.rs | 15 +++++++++++++++ compiler/rustc_attr_ir/src/data_structures.rs | 2 +- tests/ui/README.md | 2 +- .../rustc_dump_ptrauth_discriminator.rs | 7 +++++++ .../rustc_dump_ptrauth_discriminator.stderr | 14 ++++++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.rs create mode 100644 tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.stderr diff --git a/compiler/rustc_attr_ir/src/attribute_docs.rs b/compiler/rustc_attr_ir/src/attribute_docs.rs index cc6e3d849d6b1..57c49df60f72a 100644 --- a/compiler/rustc_attr_ir/src/attribute_docs.rs +++ b/compiler/rustc_attr_ir/src/attribute_docs.rs @@ -167,6 +167,21 @@ const _: () = (); /// [`object_lifetime_default`]: ../rustc_middle/ty/struct.TyCtxt.html#method.object_lifetime_default const _: () = (); +#[cfg_attr(not(bootstrap), doc(attribute = "rustc_dump_ptrauth_discriminator"))] +/// Dumps the function pointer type discriminator of the annotated function. +/// +/// The discriminator is computed from an encoding of the function's signature (function type). The +/// `ptrauth_encoding` argument prints this encoding, while the `ptrauth_hash` argument prints the +/// resulting discriminator hash. +/// +/// See [`AttributeKind::RustcDumpPtrauthDiscriminator`] for the internal representation of this +/// attribute. +/// +/// # Example +/// +#[doc = include_example!("rustc_dump_ptrauth_discriminator")] +const _: () = (); + #[doc(attribute = "rustc_dump_symbol_name")] /// Dumps the symbol name of the annotated item, also demangling it if necessary. /// diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index aced14e9bb0d0..6e344fa1c2911 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -1256,7 +1256,7 @@ pub enum AttributeKind { /// Represents the [`rustc_dump_object_lifetime_defaults`](./attribute.rustc_dump_object_lifetime_defaults.html) attribute. RustcDumpObjectLifetimeDefaults, - /// Represents the [`rustc_dump_ptrauth_discriminator`](./attribute.:rustc_dump_ptrauth_discriminator.html) attribute. + /// Represents the [`rustc_dump_ptrauth_discriminator`](./attribute.rustc_dump_ptrauth_discriminator.html) attribute. RustcDumpPtrauthDiscriminator(ThinVec), /// Represents the [`rustc_dump_symbol_name`](./attribute.rustc_dump_symbol_name.html) attribute. diff --git a/tests/ui/README.md b/tests/ui/README.md index 9570cdee8b33c..62e46cd55e496 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1118,7 +1118,7 @@ Contains only 2 tests, related to a single issue, which was about an error cause ## `tests/ui/ptrauth/`: Function pointer type discrimination -Contain only 1 tests, related to how Rust computes function pointer type discriminators for pointer authenticated code. The test uses `ptrauth_encoding` and `ptrauth_hash` attributes to obtain string encoding and hashed value respectively. +Contains only 1 tests, related to how Rust computes function pointer type discriminators for pointer authenticated code. The test uses `ptrauth_encoding` and `ptrauth_hash` attributes to obtain string encoding and hashed value respectively. ## `tests/ui/pub/`: `pub` keyword diff --git a/tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.rs b/tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.rs new file mode 100644 index 0000000000000..48b0976d3e006 --- /dev/null +++ b/tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.rs @@ -0,0 +1,7 @@ +//@ dont-require-annotations: ERROR +//@ compile-flags: --crate-type lib -Z ui-testing=no + +#![feature(rustc_attrs)] + +#[rustc_dump_ptrauth_discriminator(ptrauth_encoding, ptrauth_hash)] +extern "C" fn foo(_: i32) {} diff --git a/tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.stderr b/tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.stderr new file mode 100644 index 0000000000000..1273d7b29e324 --- /dev/null +++ b/tests/ui/attributes/doc_examples/rustc_dump_ptrauth_discriminator.stderr @@ -0,0 +1,14 @@ +error: ptrauth discriminator encoding: "FviE" + --> $DIR/rustc_dump_ptrauth_discriminator.rs:7:1 + | +7 | extern "C" fn foo(_: i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ptrauth discriminator hash: 2712 (0xa98) + --> $DIR/rustc_dump_ptrauth_discriminator.rs:7:1 + | +7 | extern "C" fn foo(_: i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +