From 9125cc1ad588cd8349838d797ba78166ee8d8c83 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:01:47 +0000 Subject: [PATCH 01/11] [PAC] Include discriminator in `FnAbi`, add `llvm.ptrauth.resign` This patch introduces the following: * Extends `FnAbi` (`callconv`) with a `ptrauth_type_discriminator` field. This field is only used when emitting pointer authentication call bundles. It is stored in `FnAbi` because the call site is not guaranteed to have access to an `Instance`, so the discriminator cannot always be computed on demand. * Adds support for `llvm.ptrauth.resign`. This intrinsic will be used when support for semantic transmute is added. * Performs a minor API redesign as groundwork for allowing call sites to modify schemas in place. --- compiler/rustc_codegen_gcc/src/builder.rs | 11 +++++++ compiler/rustc_codegen_gcc/src/common.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_gcc/src/int.rs | 1 + compiler/rustc_codegen_llvm/src/builder.rs | 32 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/common.rs | 23 +++++++------ compiler/rustc_codegen_llvm/src/context.rs | 4 +-- .../rustc_codegen_ssa/src/traits/builder.rs | 9 ++++++ .../rustc_codegen_ssa/src/traits/consts.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/misc.rs | 2 +- compiler/rustc_session/src/session.rs | 25 ++++++++++++--- compiler/rustc_target/src/callconv/mod.rs | 8 +++-- compiler/rustc_ty_utils/src/abi.rs | 6 ++++ tests/ui/abi/c-zst.aarch64-darwin.stderr | 1 + tests/ui/abi/c-zst.powerpc-linux.stderr | 1 + tests/ui/abi/c-zst.s390x-linux.stderr | 1 + tests/ui/abi/c-zst.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, 167 insertions(+), 24 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 4e4b911666143..69049ada616b0 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 abc71f450a515..29f921661ead8 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1567,6 +1567,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, '_> { @@ -2201,8 +2225,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // bundles. // Once this is resolved, we should analyze each call and skip direct calls. See the // discussion in the rust-lang issue: - let key: u32 = 0; - let discriminator: u64 = 0; + + let key: u32 = self.sess().pointer_authentication_fn_ptr_key().unwrap() as u32; + // If sess().pointer_authentication_fn_ptr_type_discrimination() is true, this contains + // the function pointer type discriminator; otherwise, it is 0. + let discriminator = fn_abi?.ptrauth_discriminator; + Some(llvm::OperandBundleBox::new( "ptrauth", &[self.const_u32(key), self.const_u64(discriminator)], diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 7a05bf718663c..c5c943002dfdc 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -30,11 +30,9 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( cx: &CodegenCx<'ll, '_>, instance: Instance<'tcx>, llfn: &'ll llvm::Value, - schema: &PointerAuthSchema, + ptrauth_schema: PointerAuthSchema, ) -> &'ll llvm::Value { - if cx.tcx.sess.pointer_authentication_functions().is_none() { - return llfn; - } + assert!(cx.tcx.sess.pointer_authentication_functions().is_some()); // Only free functions or methods let def_id = instance.def_id(); @@ -54,7 +52,7 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( return llfn; } - let addr_diversity = match schema.is_address_discriminated { + let addr_diversity = match ptrauth_schema.is_address_discriminated { PointerAuthAddressDiscriminator::HardwareAddress(true) => Some(llfn), PointerAuthAddressDiscriminator::HardwareAddress(false) => None, PointerAuthAddressDiscriminator::Synthetic(val) => { @@ -63,7 +61,12 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( Some(unsafe { llvm::LLVMConstIntToPtr(llval, llty) }) } }; - const_ptr_auth(llfn, schema.key as u32, schema.constant_discriminator as u64, addr_diversity) + const_ptr_auth( + llfn, + ptrauth_schema.key as u32, + ptrauth_schema.constant_discriminator as u64, + addr_diversity, + ) } /* @@ -179,11 +182,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { &self, global_alloc: GlobalAlloc<'tcx>, need_symbol_name: bool, - schema: Option<&PointerAuthSchema>, + ptrauth_schema: Option, ) -> Result<&'ll Value, u64> { let alloc = match global_alloc { GlobalAlloc::Function { instance, .. } => { - return Ok(self.get_fn_addr(instance, schema)); + return Ok(self.get_fn_addr(instance, ptrauth_schema)); } GlobalAlloc::Static(def_id) => { assert!(self.tcx.is_static(def_id)); @@ -405,7 +408,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { cv: Scalar, layout: abi::Scalar, llty: &'ll Type, - schema: Option<&PointerAuthSchema>, + ptrauth_schema: Option, ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { @@ -422,7 +425,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); let base_addr_space = global_alloc.address_space(self); - let base_addr = match self.alloc_to_backend(global_alloc, false, schema) { + let base_addr = match self.alloc_to_backend(global_alloc, false, ptrauth_schema) { Ok(base_addr) => base_addr, Err(base_addr) => { let val = base_addr.wrapping_add(offset.bytes()); diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 0ce3bfe07c413..2e0db574e8d62 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -987,7 +987,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 @@ -1002,7 +1002,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { // , and comment in // builder's `ptrauth_operand_bundle`. let llfn = get_fn(self, instance); - match pointer_auth_schema { + match ptrauth_schema { Some(schema) => common::maybe_sign_fn_ptr(self, instance, llfn, schema), None => llfn, } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index b7b694922bcfa..64fe767410733 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -687,4 +687,13 @@ pub trait BuilderMethods<'a, 'tcx>: fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn apply_attrs_to_cleanup_callsite(&mut self, llret: Self::Value); + + fn ptrauth_resign( + &mut self, + value: Self::Value, + old_key: u32, + old_discriminator: u64, + new_key: u32, + new_discriminator: u64, + ) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index b4eba38d39c19..b45b5667be6c9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -47,7 +47,7 @@ pub trait ConstCodegenMethods: BackendTypes { cv: Scalar, layout: abi::Scalar, llty: Self::Type, - schema: Option<&PointerAuthSchema>, + ptrauth_schema: Option, ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index add7128a2974b..3d1a931a12e83 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -22,7 +22,7 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn get_fn_addr( &self, instance: Instance<'tcx>, - pointer_auth_schema: Option<&PointerAuthSchema>, + ptrauth_schema: Option, ) -> Self::Value; fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 65c0229184db6..9adbb44fc405a 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, @@ -1256,12 +1259,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 edc23b6c50b45..893ac55c8f50e 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -700,12 +700,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) @@ -713,6 +716,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() } } @@ -1038,6 +1042,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(ArgAbi<'_, usize>, 64); - 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 448167ff90874..8c8453e48a273 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, @@ -613,6 +614,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 e5cad2199491b..d0a5bab9e8073 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -71,6 +71,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 e5cad2199491b..d0a5bab9e8073 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -71,6 +71,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 e5cad2199491b..715fae66f3c93 100644 --- a/tests/ui/abi/c-zst.sparc-linux.stderr +++ b/tests/ui/abi/c-zst.sparc-linux.stderr @@ -71,6 +71,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 e5cad2199491b..715fae66f3c93 100644 --- a/tests/ui/abi/c-zst.sparc-none.stderr +++ b/tests/ui/abi/c-zst.sparc-none.stderr @@ -71,6 +71,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 e5cad2199491b..d0a5bab9e8073 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -71,6 +71,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 e5cad2199491b..d0a5bab9e8073 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 @@ -71,6 +71,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 1793674fa462a..f71e639b16f4f 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -106,6 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -187,6 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -258,6 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -336,6 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -402,6 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -482,6 +487,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -556,6 +562,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -628,6 +635,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -694,6 +702,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -766,6 +775,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -832,6 +842,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -926,6 +937,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 29ec7846101f1..f3bd161b301b5 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -106,6 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -187,6 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -258,6 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -336,6 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -402,6 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -482,6 +487,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -556,6 +562,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -628,6 +635,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -694,6 +702,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -766,6 +775,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -832,6 +842,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -926,6 +937,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 29ec7846101f1..f3bd161b301b5 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -106,6 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -187,6 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -258,6 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -336,6 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -402,6 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -482,6 +487,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -556,6 +562,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -628,6 +635,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -694,6 +702,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -766,6 +775,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } right ABI = FnAbi { args: [ @@ -832,6 +842,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -926,6 +937,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr index 65818feab4297..927594534d9b8 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr @@ -69,6 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -146,6 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -223,6 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -300,6 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -377,6 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -454,6 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr index cbe389c42d40a..113ad20e16bc4 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr @@ -69,6 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -146,6 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -223,6 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -300,6 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -377,6 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -454,6 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index 5821e6279bb85..75504c8546498 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -84,6 +84,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 | @@ -175,6 +176,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 c9e77ac941901..f9dc652b53440 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -71,6 +71,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 04320a5312361..7471991866eef 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 @@ -71,6 +71,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 | @@ -152,6 +153,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 | @@ -233,6 +235,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 0e38313b5d7f1b36637d9787adc7b21d21114452 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 20 Aug 2026 06:47:34 +0000 Subject: [PATCH 02/11] [PAC] Use Option in FnAbi's discriminator field --- compiler/rustc_codegen_gcc/src/int.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 7 +++--- compiler/rustc_target/src/callconv/mod.rs | 4 ++-- compiler/rustc_ty_utils/src/abi.rs | 4 ++-- tests/ui/abi/c-zst.aarch64-darwin.stderr | 2 +- tests/ui/abi/c-zst.powerpc-linux.stderr | 2 +- tests/ui/abi/c-zst.s390x-linux.stderr | 2 +- tests/ui/abi/c-zst.sparc64-linux.stderr | 2 +- tests/ui/abi/c-zst.x86_64-linux.stderr | 2 +- .../ui/abi/c-zst.x86_64-pc-windows-gnu.stderr | 2 +- tests/ui/abi/debug.generic.stderr | 24 +++++++++---------- tests/ui/abi/debug.loongarch64.stderr | 24 +++++++++---------- tests/ui/abi/debug.riscv64.stderr | 24 +++++++++---------- .../x86-64-sysv64-arg-ext.apple.stderr | 12 +++++----- .../x86-64-sysv64-arg-ext.other.stderr | 12 +++++----- tests/ui/abi/pass-indirectly-attr.stderr | 4 ++-- tests/ui/abi/sysv64-zst.stderr | 2 +- .../pass-by-value-abi.aarch64.stderr | 2 +- .../c-variadic/pass-by-value-abi.win.stderr | 2 +- .../pass-by-value-abi.x86_64.stderr | 6 ++--- 20 files changed, 71 insertions(+), 70 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index 69049ada616b0..abfc2a9999727 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 29f921661ead8..c7eac8e72a2de 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -2227,9 +2227,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // discussion in the rust-lang issue: let key: u32 = self.sess().pointer_authentication_fn_ptr_key().unwrap() as u32; - // If sess().pointer_authentication_fn_ptr_type_discrimination() is true, this contains - // the function pointer type discriminator; otherwise, it is 0. - let discriminator = fn_abi?.ptrauth_discriminator; + // If sess().pointer_authentication_fn_ptr_type_discrimination() is enabled, this contains + // the function pointer type discriminator; otherwise, it is None. LLVM expects a u64 here, + // so use 0 when no discriminator is present. + let discriminator = fn_abi?.ptrauth_discriminator.unwrap_or(0); Some(llvm::OperandBundleBox::new( "ptrauth", diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 893ac55c8f50e..8ea207a4bba56 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -701,7 +701,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. @@ -1042,6 +1042,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(ArgAbi<'_, usize>, 64); - static_assert_size!(FnAbi<'_, usize>, 96); + static_assert_size!(FnAbi<'_, usize>, 104); // tidy-alphabetical-end } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 8c8453e48a273..867cec3d8fee7 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -615,9 +615,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 d0a5bab9e8073..715fae66f3c93 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -71,7 +71,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 d0a5bab9e8073..715fae66f3c93 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -71,7 +71,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 d0a5bab9e8073..715fae66f3c93 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -71,7 +71,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 d0a5bab9e8073..715fae66f3c93 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 @@ -71,7 +71,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 f71e639b16f4f..6149a9aa01b7b 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -106,7 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:31:1 | @@ -188,7 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | @@ -260,7 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:40:1 | @@ -339,7 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -406,7 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:59:1 | @@ -487,7 +487,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: [ @@ -562,7 +562,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:62:1 | @@ -635,7 +635,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: [ @@ -702,7 +702,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 | @@ -775,7 +775,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: [ @@ -842,7 +842,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:69:1 | @@ -937,7 +937,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index f3bd161b301b5..8fd106815487d 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -106,7 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:31:1 | @@ -188,7 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | @@ -260,7 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:40:1 | @@ -339,7 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -406,7 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:59:1 | @@ -487,7 +487,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: [ @@ -562,7 +562,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:62:1 | @@ -635,7 +635,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: [ @@ -702,7 +702,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 | @@ -775,7 +775,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: [ @@ -842,7 +842,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:69:1 | @@ -937,7 +937,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index f3bd161b301b5..8fd106815487d 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -106,7 +106,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:31:1 | @@ -188,7 +188,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:37:1 | @@ -260,7 +260,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:40:1 | @@ -339,7 +339,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } right ABI = FnAbi { args: [ @@ -406,7 +406,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:59:1 | @@ -487,7 +487,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: [ @@ -562,7 +562,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:62:1 | @@ -635,7 +635,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: [ @@ -702,7 +702,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 | @@ -775,7 +775,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: [ @@ -842,7 +842,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:69:1 | @@ -937,7 +937,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr index 927594534d9b8..df49fbfe3d272 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr @@ -69,7 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -147,7 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -225,7 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -303,7 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -381,7 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -459,7 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr index 113ad20e16bc4..7ceb6a2092af2 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr @@ -69,7 +69,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -147,7 +147,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -225,7 +225,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -303,7 +303,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -381,7 +381,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -459,7 +459,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, - ptrauth_discriminator: 0, + ptrauth_discriminator: None, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index 75504c8546498..8431b6ed6dea0 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -84,7 +84,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 | @@ -176,7 +176,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 f9dc652b53440..f827817dccc55 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -71,7 +71,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 7471991866eef..afa436de5b83b 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 @@ -71,7 +71,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 | @@ -153,7 +153,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 | @@ -235,7 +235,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 224785690ac06f2f4916a759894cd0e959d189c5 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 10 Sep 2026 13:33:36 +0000 Subject: [PATCH 03/11] [PAC] repr(transparent) and correct handling of Option in encoder Also tighten the handling of enums. --- .../rustc_middle/src/ptrauth/discriminator.rs | 72 ++++++++++++------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index 7501f1b072188..30226b8eebe07 100644 --- a/compiler/rustc_middle/src/ptrauth/discriminator.rs +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -81,6 +81,7 @@ use rustc_session::PointerAuthSchema; use rustc_span::sym; use crate::ptrauth::llvm_siphash::llvm_pointer_auth_stable_siphash; +use crate::ty::layout::LayoutCx; /// Types that can serve as a source for function pointer type discrimination. /// @@ -317,35 +318,46 @@ enum ClangDiscTy<'tcx> { Void, } -// Canonicalize Option-wrapped pointer types used to model C nullable pointers. +// Canonicalize types that are ABI-compatible with C's nullable pointer +// convention, so the rest of this encoder can treat them like the corresponding +// plain pointer type. // -// Rust and Clang should compute identical discriminators for equivalent C APIs. -// Clang does not distinguish nullable from non-nullable pointer types when -// computing function pointer authentication discriminators, so -// `Option` and `Option<*mut T>` are encoded identically to their -// underlying pointer types. +// Rust guarantees the null-pointer optimization for references, function +// pointers, Box, NonNull, and NonZero*. `Option` and `Option<&T>` are +// therefore unwrapped here. `Option<*mut T>` and `Option<*const T>` are +// deliberately left unchanged: raw pointers are not covered by the NPO +// guarantee and are handled by the general `Adt` arm in `to_clang_disc_ty`. // -// Although `Option<*mut T>` is not considered FFI-safe by Rust and triggers the -// `improper_ctypes`/`improper_ctypes_definitions` lints, this is a warning -// rather than a hard error. Canonicalizing it here preserves Clang-compatible -// discriminator computation. -// -// Please see the following tests for sample use cases: -// pauth-fn-ptr-type-discrimination-option-callback.rs, -// pauth-fn-ptr-type-discrimination-option-return.rs and pauth-fn-ptr-type-discrimination-option.rs -fn canonicalize_c_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Adt(def, args) = ty.kind() - && tcx.is_diagnostic_item(sym::Option, def.did()) - { - let inner = args.type_at(0); - - match inner.kind() { - ty::FnPtr(..) | ty::RawPtr(..) => return inner, - _ => {} +// Also peels `repr(transparent)` wrappers to canonicalize them to their +// underlying type. +fn canonicalize_c_type<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> { + loop { + let before = ty; + + if let ty::Adt(def, args) = ty.kind() + && tcx.is_diagnostic_item(sym::Option, def.did()) + { + let inner = args.type_at(0); + if let ty::FnPtr(..) | ty::Ref(..) = inner.kind() { + ty = inner; + } } - } - ty + // Only ADTs can be repr(transparent); skip the layout query entirely + // for everything else. + if matches!(ty.kind(), ty::Adt(..)) { + let typing_env = ty::TypingEnv::fully_monomorphized(); + + if let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) { + let cx = LayoutCx::new(tcx, typing_env); + ty = layout.peel_transparent_wrappers(&cx).ty; + } + } + + if ty == before { + return ty; + } + } } /// Lowers a Rust type into a Clang-compatible discriminator type. @@ -388,8 +400,14 @@ fn to_clang_disc_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ClangDiscTy<'tcx> // arrays ignore size ty::Array(elem, _) => ClangDiscTy::Array { elem: *elem }, - // enums to integer collapse - ty::Adt(def, _) if def.is_enum() => ClangDiscTy::EnumLikeInt, + // enums to integer collapse - mirrors Clang's Type::Enum handling, + // which recurses into the enum's underlying integer type per C11 + // 6.7.2.2p4. + // A non-niche, data-carrying enum (e.g. Option<*mut T>) is not an + // "enumerated type" in the C11 sense, such enums fall through to the + // general Adt(_) => AdtName(..) arm below instead. + ty::Adt(def, _) if def.is_enum() && def.is_payloadfree() => ClangDiscTy::EnumLikeInt, + // simd vectors ty::Adt(def, args) if def.repr().simd() => { // Clang encodes SIMD vectors by their total size From dc274910dec034e09e14ef2444437afd7cc6db9e Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:16:40 +0000 Subject: [PATCH 04/11] [PAC] Enable support for FPTR_TYPE_DISCR in ABI Version Also remove error messages/tests that used to guarded it. --- compiler/rustc_session/src/diagnostics.rs | 6 ------ compiler/rustc_session/src/session.rs | 21 +++++-------------- ...on_not_supported_pointer_authentication.rs | 12 ----------- ...ot_supported_pointer_authentication.stderr | 4 ---- 4 files changed, 5 insertions(+), 38 deletions(-) delete mode 100644 tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs delete mode 100644 tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr diff --git a/compiler/rustc_session/src/diagnostics.rs b/compiler/rustc_session/src/diagnostics.rs index 4ec8ca42edbee..17395e126eda1 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -384,12 +384,6 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } -#[derive(Diagnostic)] -#[diag("function pointer type discrimination is not supported")] -pub(crate) struct PointerAuthenticationTypeDiscriminationNotSupportedForTarget<'a> { - pub(crate) target_triple: &'a TargetTuple, -} - #[derive(Diagnostic)] #[diag( "`-Z pointer-authentication` is not supported for target {$target_triple} and will be ignored" diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9adbb44fc405a..3ab0c478d08c0 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 } @@ -1562,19 +1564,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(diagnostics::LinkerPluginToWindowsNotSupported); } - if sess - .pointer_auth_config - .as_ref() - .and_then(|cfg| cfg.function_pointers.as_ref()) - .is_some_and(|schema| matches!(schema.discrimination_kind, PointerAuthDiscrimination::Type)) - { - sess.dcx().emit_err( - diagnostics::PointerAuthenticationTypeDiscriminationNotSupportedForTarget { - target_triple: &sess.opts.target_triple, - }, - ); - } - if sess.target.cfg_abi != CfgAbi::Pauthtest && !sess.opts.unstable_opts.pointer_authentication.is_empty() { diff --git a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs deleted file mode 100644 index 6838e749fd333..0000000000000 --- a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ ignore-backends: gcc -//@ check-fail -//@ needs-llvm-components: aarch64 - -//@ compile-flags: -Zpointer-authentication=+function-pointer-type-discrimination --crate-type=lib --target aarch64-unknown-linux-pauthtest - -#![feature(no_core)] -#![no_std] -#![no_main] -#![no_core] - -//~? ERROR function pointer type discrimination is not supported diff --git a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr deleted file mode 100644 index c040b0cb61f66..0000000000000 --- a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr +++ /dev/null @@ -1,4 +0,0 @@ -error: function pointer type discrimination is not supported - -error: aborting due to 1 previous error - From ae51e1a3c6e1b8bac02fcdafd840ae02fca1e77e Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:59:12 +0000 Subject: [PATCH 05/11] [PAC] Support type discriminators in static allocations The codegen now walks the layout of static initializer types to find extern "C" function pointer fields, computes their type discriminators, and applies those discriminators when emitting authenticated function pointer relocations. Also make sure that type discrimination is never applied to init/fini entries. --- compiler/rustc_codegen_gcc/src/common.rs | 4 +- compiler/rustc_codegen_llvm/src/asm.rs | 8 +- compiler/rustc_codegen_llvm/src/common.rs | 20 ++- compiler/rustc_codegen_llvm/src/consts.rs | 169 +++++++++++++++++- .../rustc_codegen_ssa/src/traits/consts.rs | 5 +- 5 files changed, 194 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index 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 b20a89859388e..b324bce2c13e0 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -166,7 +166,8 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ConstScalar::Ptr(ptr, _) => { let (prov, _) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); - let value = self.cx.alloc_to_backend(global_alloc, false, None).unwrap(); + let value = + self.cx.alloc_to_backend(global_alloc, false, None, None).unwrap(); inputs.push(value); op_idx.insert(idx, constraints.len()); constraints.push("s".to_string()); @@ -456,8 +457,9 @@ impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { ConstScalar::Ptr(ptr, _) => { let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); - let llval = - self.alloc_to_backend(global_alloc, true, None).unwrap(); + let llval = self + .alloc_to_backend(global_alloc, true, None, None) + .unwrap(); self.add_compiler_used_global(llval); let symbol = llvm::build_string(|s| unsafe { diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index c5c943002dfdc..7f4a49dcef818 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -4,11 +4,12 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _}; +use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _, Size}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_crate_store::DllImport; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_hashes::Hash128; use rustc_hir::def::DefKind; @@ -183,6 +184,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { global_alloc: GlobalAlloc<'tcx>, need_symbol_name: bool, ptrauth_schema: Option, + ptrauth_discriminators: Option<&FxHashMap>, ) -> Result<&'ll Value, u64> { let alloc = match global_alloc { GlobalAlloc::Function { instance, .. } => { @@ -229,7 +231,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } }; - let init = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ptrauth_discriminators, + ); let alloc = alloc.inner(); if need_symbol_name { @@ -409,6 +417,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { layout: abi::Scalar, llty: &'ll Type, ptrauth_schema: Option, + ptrauth_discriminators: Option<&FxHashMap>, ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { @@ -425,7 +434,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { let (prov, offset) = ptr.prov_and_relative_offset(); let global_alloc = self.tcx.global_alloc(prov.alloc_id()); let base_addr_space = global_alloc.address_space(self); - let base_addr = match self.alloc_to_backend(global_alloc, false, ptrauth_schema) { + let base_addr = match self.alloc_to_backend( + global_alloc, + false, + ptrauth_schema, + ptrauth_discriminators, + ) { Ok(base_addr) => base_addr, Err(base_addr) => { let val = base_addr.wrapping_add(offset.bytes()); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index bbb1141a92e89..93344b8160671 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -3,6 +3,7 @@ use std::ops::Range; use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::attrs::Linkage; use rustc_hir::attrs::lang_items::LangItem; use rustc_hir::def::DefKind; @@ -13,8 +14,9 @@ use rustc_middle::mir::interpret::{ read_target_uint, }; use rustc_middle::mono::MonoItem; +use rustc_middle::ptrauth::ptrauth_compute_fn_ptr_type_discriminator_for; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{self, Instance}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_span::{Symbol, bug, span_bug}; use rustc_target::spec::Arch; use tracing::{debug, instrument, trace}; @@ -36,11 +38,135 @@ pub(crate) enum IsInitOrFini { Yes, No, } + +/// Recursively walks a type layout and records the offsets of all extern "C" +/// function pointer fields together with their computed type discriminators. +/// +/// Traversal currently supports: +/// - references +/// - direct function pointers +/// - structs +/// - tuples +/// - arrays +/// +/// Offsets are accumulated relative to the containing object. +fn collect_fn_ptr_discriminators<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> FxHashMap { + let mut map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map); + + map +} + +fn collect_fn_ptr_discriminators_inner<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + base_offset: Size, + map: &mut FxHashMap, +) { + // Direct function pointer. + if let Some(disc) = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, ty) { + map.insert(base_offset, disc.into()); + + return; + } + + match ty.kind() { + ty::Ref(_, pointee, _) => { + collect_fn_ptr_discriminators_inner(tcx, typing_env, *pointee, base_offset, map); + } + ty::Adt(def, args) if def.is_struct() => { + let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { + return; + }; + + let variant = def.non_enum_variant(); + + for (idx, field_def) in variant.fields.iter_enumerated() { + let field_ty = tcx.normalize_erasing_regions(typing_env, field_def.ty(tcx, args)); + + let field_offset = layout.fields.offset(idx.into()); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + field_ty, + base_offset + field_offset, + map, + ); + } + } + ty::Tuple(fields) => { + let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { + return; + }; + + for (idx, field_ty) in fields.iter().enumerate() { + let field_offset = layout.fields.offset(idx); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + field_ty, + base_offset + field_offset, + map, + ); + } + } + ty::Array(elem_ty, len) => { + let count = match len.try_to_target_usize(tcx) { + Some(v) => v, + None => return, + }; + + let Ok(elem_layout) = tcx.layout_of(typing_env.as_query_input(*elem_ty)) else { + return; + }; + + let stride = elem_layout.size; + + // Collect discriminator of one element, so we don't have to recompute it for all the + // elements in the array. + let mut elem_map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + *elem_ty, + Size::ZERO, + &mut elem_map, + ); + + // SAFETY: We immediately collect into a Vec and sort by offset. + // The HashMap iteration order is irrelevant and must not affect determinism. + #[allow(rustc::potential_query_instability)] + let mut entries: Vec<(Size, u64)> = elem_map.into_iter().collect(); + entries.sort_unstable_by_key(|(offset, _)| *offset); + + // Replicate for every array slot. + for i in 0..count { + let elem_base = base_offset + stride * i; + + for (inner_offset, discr) in entries.iter().copied() { + map.insert(elem_base + inner_offset, discr); + } + } + } + _ => {} + } +} + pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, alloc: &Allocation, is_static: IsStatic, is_init_fini: IsInitOrFini, + ptrauth_discriminators: Option<&FxHashMap>, ) -> &'ll Value { // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be @@ -120,7 +246,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( as u64; let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); - let schema = if cx.sess().pointer_authentication() { + let mut schema = if cx.sess().pointer_authentication() { match is_init_fini { IsInitOrFini::Yes => cx.sess().pointer_authentication_init_fini(), IsInitOrFini::No => cx.sess().pointer_authentication_functions(), @@ -128,6 +254,16 @@ pub(crate) fn const_alloc_to_llvm<'ll>( } else { None }; + let discr = + ptrauth_discriminators.as_ref().and_then(|m| m.get(&Size::from_bytes(offset as u64))); + + // Init/fini entries must not participate in function pointer type discrimination, they use + // a dedicated constant value (ptrauth_string_discriminator("init_fini") which is: 0xd9d4). + if let (Some(schema), Some(discr)) = (schema.as_mut(), discr) + && is_init_fini == IsInitOrFini::No + { + schema.constant_discriminator = *discr as u16; + } llvals.push(cx.scalar_to_backend_with_pac( InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx), Scalar::Initialized { @@ -136,6 +272,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( }, cx.type_ptr_ext(address_space), schema, + ptrauth_discriminators, )); next_offset = offset + pointer_size_bytes; } @@ -159,6 +296,15 @@ fn codegen_static_initializer<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { + let ptrauth_discriminators = if cx.sess().pointer_authentication_fn_ptr_type_discrimination() { + let instance = Instance::mono(cx.tcx, def_id); + let ty = instance.ty(cx.tcx, cx.typing_env()); + + Some(collect_fn_ptr_discriminators(cx.tcx, cx.typing_env(), ty)) + } else { + None + }; + let alloc = cx.tcx.eval_static_initializer(def_id)?; let attrs = cx.tcx.codegen_fn_attrs(def_id); // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion @@ -174,7 +320,16 @@ fn codegen_static_initializer<'ll, 'tcx>( } }) .unwrap_or(IsInitOrFini::No); - Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc)) + Ok(( + const_alloc_to_llvm( + cx, + alloc.inner(), + IsStatic::Yes, + is_in_init_fini, + ptrauth_discriminators.as_ref(), + ), + alloc, + )) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -911,7 +1066,13 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the // same `ConstAllocation`? - let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); + // FIXME(jchlanda): Add support for pointer authentication type discrimination. + // `static_addr_of` only receives a `ConstAllocation`, so it does not have the type + // information needed to compute function pointer type discriminators. We'll likely need + // to either compute the discriminator map at callers that still know the Rust type, or + // extend this API to accept the required type information. See + // `codegen_static_initializer` for an example of how the discriminator map is computed. + let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No, None); let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind); // static_addr_of_impl returns the bare global variable, which might not be in the default diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index b45b5667be6c9..7f82ebda2e5fb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -1,4 +1,6 @@ use rustc_abi as abi; +use rustc_abi::Size; +use rustc_data_structures::fx::FxHashMap; use rustc_middle::mir::interpret::Scalar; use rustc_session::PointerAuthSchema; @@ -40,7 +42,7 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option; fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value { - self.scalar_to_backend_with_pac(cv, layout, llty, None) + self.scalar_to_backend_with_pac(cv, layout, llty, None, None) } fn scalar_to_backend_with_pac( &self, @@ -48,6 +50,7 @@ pub trait ConstCodegenMethods: BackendTypes { layout: abi::Scalar, llty: Self::Type, ptrauth_schema: Option, + ptrauth_discriminators: Option<&FxHashMap>, ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; From 1f6474c22104753711efdd418bb02de0e989fde1 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Wed, 29 Jul 2026 14:18:57 +0000 Subject: [PATCH 06/11] [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 | 149 ++-------------- 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(+), 151 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 8576dfe079163..765a9a96f25af 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -7,6 +7,7 @@ use rustc_abi::{self as abi, Align, HasDataLayout, Primitive, Size, WrappingRang use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, }; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; use rustc_hir::def_id::LOCAL_CRATE; @@ -61,7 +62,12 @@ fn set_global_alignment<'gcc, 'tcx>( } impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { - fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> RValue<'gcc> { + fn static_addr_of( + &self, + alloc: ConstAllocation<'_>, + kind: Option<&str>, + _ptrauth_discriminators: Option<&FxHashMap>, + ) -> RValue<'gcc> { let cv = const_alloc_to_gcc(self, alloc); let align = alloc.inner().align; diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 93344b8160671..7d55696bb4bf1 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -14,9 +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::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}; @@ -39,128 +39,6 @@ pub(crate) enum IsInitOrFini { No, } -/// Recursively walks a type layout and records the offsets of all extern "C" -/// function pointer fields together with their computed type discriminators. -/// -/// Traversal currently supports: -/// - references -/// - direct function pointers -/// - structs -/// - tuples -/// - arrays -/// -/// Offsets are accumulated relative to the containing object. -fn collect_fn_ptr_discriminators<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ty: Ty<'tcx>, -) -> FxHashMap { - let mut map = FxHashMap::default(); - - collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map); - - map -} - -fn collect_fn_ptr_discriminators_inner<'tcx>( - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ty: Ty<'tcx>, - base_offset: Size, - map: &mut FxHashMap, -) { - // Direct function pointer. - if let Some(disc) = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, ty) { - map.insert(base_offset, disc.into()); - - return; - } - - match ty.kind() { - ty::Ref(_, pointee, _) => { - collect_fn_ptr_discriminators_inner(tcx, typing_env, *pointee, base_offset, map); - } - ty::Adt(def, args) if def.is_struct() => { - let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { - return; - }; - - let variant = def.non_enum_variant(); - - for (idx, field_def) in variant.fields.iter_enumerated() { - let field_ty = tcx.normalize_erasing_regions(typing_env, field_def.ty(tcx, args)); - - let field_offset = layout.fields.offset(idx.into()); - - collect_fn_ptr_discriminators_inner( - tcx, - typing_env, - field_ty, - base_offset + field_offset, - map, - ); - } - } - ty::Tuple(fields) => { - let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { - return; - }; - - for (idx, field_ty) in fields.iter().enumerate() { - let field_offset = layout.fields.offset(idx); - - collect_fn_ptr_discriminators_inner( - tcx, - typing_env, - field_ty, - base_offset + field_offset, - map, - ); - } - } - ty::Array(elem_ty, len) => { - let count = match len.try_to_target_usize(tcx) { - Some(v) => v, - None => return, - }; - - let Ok(elem_layout) = tcx.layout_of(typing_env.as_query_input(*elem_ty)) else { - return; - }; - - let stride = elem_layout.size; - - // Collect discriminator of one element, so we don't have to recompute it for all the - // elements in the array. - let mut elem_map = FxHashMap::default(); - - collect_fn_ptr_discriminators_inner( - tcx, - typing_env, - *elem_ty, - Size::ZERO, - &mut elem_map, - ); - - // SAFETY: We immediately collect into a Vec and sort by offset. - // The HashMap iteration order is irrelevant and must not affect determinism. - #[allow(rustc::potential_query_instability)] - let mut entries: Vec<(Size, u64)> = elem_map.into_iter().collect(); - entries.sort_unstable_by_key(|(offset, _)| *offset); - - // Replicate for every array slot. - for i in 0..count { - let elem_base = base_offset + stride * i; - - for (inner_offset, discr) in entries.iter().copied() { - map.insert(elem_base + inner_offset, discr); - } - } - } - _ => {} - } -} - pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, alloc: &Allocation, @@ -300,7 +178,7 @@ fn codegen_static_initializer<'ll, 'tcx>( let instance = Instance::mono(cx.tcx, def_id); let ty = instance.ty(cx.tcx, cx.typing_env()); - Some(collect_fn_ptr_discriminators(cx.tcx, cx.typing_env(), ty)) + Some(ptrauth_collect_fn_ptr_discriminators(cx.tcx, cx.typing_env(), ty)) } else { None }; @@ -1063,16 +941,21 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { /// /// The pointer will always be in the default address space. If global variables default to a /// different address space, an addrspacecast is inserted. - fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { + fn static_addr_of( + &self, + alloc: ConstAllocation<'_>, + kind: Option<&str>, + ptrauth_discriminators: Option<&FxHashMap>, + ) -> &'ll Value { // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the // same `ConstAllocation`? - // FIXME(jchlanda): Add support for pointer authentication type discrimination. - // `static_addr_of` only receives a `ConstAllocation`, so it does not have the type - // information needed to compute function pointer type discriminators. We'll likely need - // to either compute the discriminator map at callers that still know the Rust type, or - // extend this API to accept the required type information. See - // `codegen_static_initializer` for an example of how the discriminator map is computed. - let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No, None); + let cv = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ptrauth_discriminators, + ); let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind); // static_addr_of_impl returns the bare global variable, which might not be in the default diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 9c024cacc35c9..b88a1b1c147f1 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -114,7 +114,10 @@ pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( let vtable_alloc_id = tcx.vtable_allocation((ty, trait_ref)); let vtable_allocation = tcx.global_alloc(vtable_alloc_id).unwrap_memory(); - let vtable = cx.static_addr_of(vtable_allocation, Some("vtable")); + // Vtables cannot contain extern "C"/"System" function pointers (asserted in + // vtable_allocation_provider), so they do not require function pointer type + // discriminators. + let vtable = cx.static_addr_of(vtable_allocation, Some("vtable"), None); cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable); cx.create_vtable_debuginfo(ty, trait_ref, vtable); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index d11ba53e58c89..bec7aea97b5c9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -8,6 +8,7 @@ use rustc_abi::{ use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::mir::interpret::{Pointer, Scalar, alloc_range}; use rustc_middle::mir::{self, ConstValue}; +use rustc_middle::ptrauth::ptrauth_collect_fn_ptr_discriminators; use rustc_middle::ty::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 a71a57f02c82a..7652a656db8a3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/retag.rs +++ b/compiler/rustc_codegen_ssa/src/mir/retag.rs @@ -271,7 +271,9 @@ impl<'a, 'tcx, V> RetagPlan { let global_alloc = tcx.global_alloc(alloc_id); let global_mem = global_alloc.unwrap_memory(); - bx.cx().static_addr_of(global_mem, None) + // The range table contains only integer data, not pointer relocations, so no ptrauth + // discriminators are needed. + bx.cx().static_addr_of(global_mem, None, None) } } diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index c726213025350..20394c98812c1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -1,10 +1,18 @@ +use rustc_abi::Size; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_middle::mir::interpret::ConstAllocation; use super::BackendTypes; pub trait StaticCodegenMethods: BackendTypes { - fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> Self::Value; + fn static_addr_of( + &self, + alloc: ConstAllocation<'_>, + kind: Option<&str>, + ptrauth_discriminators: Option<&FxHashMap>, + ) -> Self::Value; + fn codegen_static(&mut self, def_id: DefId); } diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs index 30226b8eebe07..b15c66f806172 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 d2b32a9e7497bff071a5682db4cb66ff3021f6d6 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Thu, 10 Sep 2026 14:02:00 +0000 Subject: [PATCH 07/11] [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 bec7aea97b5c9..62b61a1166e6e 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 b15c66f806172..f56b2bf96c760 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 291b33ad4e1995e7a063cb419464bba4cf6bb567 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 21 Sep 2026 10:18:24 +0000 Subject: [PATCH 08/11] [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 | 1 + .../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 | 43 ++ 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, 951 insertions(+), 44 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 712ed41ae7759..70d35bdc9477e 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -590,6 +590,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 { PeekMaybeInit, @@ -1252,6 +1258,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 6f05f763f2ada..e076cc4fc0f4b 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, 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 22a4a069b75c7..61349c0ca93c7 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 0f96eb7f158ed..5c0b337e5995e 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -396,6 +396,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 610eb5fd099d2..8f8fa3f57b5f1 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -35,7 +35,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; @@ -1187,6 +1187,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 f56b2bf96c760..558a08a002620 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,9 +434,30 @@ fn compute_fn_ptr_type_discriminator<'tcx>( enc.push(b'E'); - let hash = 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) +} - hash.into() +/// 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. @@ -454,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, @@ -471,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(&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; } @@ -533,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, @@ -545,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 } @@ -611,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 4926ec9872d3b..e6b95fe37bb6b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -346,6 +346,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..68368aaf73917 --- /dev/null +++ b/compiler/rustc_passes/src/ptrauth_test.rs @@ -0,0 +1,43 @@ +use rustc_hir::attrs::RustcDumpPtrauthDiscriminatorKind; +use rustc_hir::def::DefKind; +use rustc_hir::find_attr; +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 b490362a34ee4..51b585ed8a678 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1678,6 +1678,8 @@ symbols! { ptr_write_bytes, ptr_write_unaligned, ptr_write_volatile, + ptrauth_encoding, + ptrauth_hash, pub_macro_rules, pub_restricted, public, @@ -1834,6 +1836,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 039b44d2e65ff75e887192d86392a1bd8a3e7e95 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 13:20:29 +0000 Subject: [PATCH 09/11] [PAC] Function pointer type discrimination for transmutes Implement pointer authentication resigning for function pointer transmutes that differ in their discriminators. Resigning only happens for function pointers (their transparent wrappers and Option). Aggregates (even those containing function pointer members) are deliberately kept as opaque values with no resigning. --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 318 ++++++++++++++++++- 1 file changed, 306 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 3b33cf5a17602..95517eb3a7097 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,9 +1,12 @@ use std::assert_matches; use itertools::Itertools as _; -use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; +use rustc_abi::{self as abi, BackendRepr, ExternAbi, FIRST_VARIANT}; use rustc_index::IndexVec; use rustc_middle::mir; +use rustc_middle::ptrauth::{ + ptrauth_clone_discriminated_schema_for, ptrauth_compute_fn_ptr_type_discriminator_for, +}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::consts::ConstExt; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; @@ -19,6 +22,13 @@ use crate::common::{IntPredicate, TypeKind}; use crate::traits::*; use crate::{MemFlags, base}; +/// Type metadata used when applying pointer authentication semantics during +/// transmute lowering. +struct TransmuteInfo<'tcx> { + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx>, +} + impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn try_codegen_const_aggregate_as_immediate( &mut self, @@ -93,6 +103,236 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { true } + /// Lowers a transmute of an SSA operand while preserving pointer authentication semantics. + /// When the source and destination are both, or transparently wrap, function pointer types + /// with different type discriminators, the resulting pointer is re-signed. + /// + /// Aggregates that contain a function pointer field are intentionally treated as an ordinary + /// opaque-values, for which no pointer resigning is performed. + fn ptrauth_codegen_transmute_operand( + &mut self, + bx: &mut Bx, + operand: OperandRef<'tcx, Bx::Value>, + cast: TyAndLayout<'tcx>, + ) -> OperandValue { + let val = self.codegen_transmute_operand(bx, operand, cast); + + let OperandValue::Immediate(ptr) = val else { + return val; + }; + + let src_semantic = self.ptrauth_canonicalize_fn_ptr_layout(operand.layout); + let dst_semantic = self.ptrauth_canonicalize_fn_ptr_layout(cast); + + // Neither side is a function pointer - early return. + if src_semantic.is_none() && dst_semantic.is_none() { + return val; + } + + let info = TransmuteInfo { + src_ty: src_semantic.map_or(operand.layout.ty, |(ty, _)| ty), + dst_ty: dst_semantic.map_or(cast.ty, |(ty, _)| ty), + }; + + let nullable = src_semantic.is_some_and(|(_, n)| n) || dst_semantic.is_some_and(|(_, n)| n); + + let ptr = if nullable { + self.ptrauth_resign_transmuted_nullable_fn_ptr(bx, ptr, info) + } else { + self.ptrauth_resign_transmuted_fn_ptr(bx, ptr, info) + }; + + OperandValue::Immediate(ptr) + } + + /// Applies pointer-authentication type discriminator correction for a function pointer value + /// being transmuted between two types. + /// + /// If the source and destination types have different type discriminator values, the pointer + /// must be resigned using `llvm.ptrauth.resign` intrinsic. + /// + /// A discriminator value of `0` is used to represent non-function-pointer + /// or "raw pointer" values: + /// ```text + /// static mut CPTR: *const u8 = 0 as *const u8; + /// ... = mem::transmute::<*const u8, unsafe extern "C" fn()>(CPTR); + /// ``` + /// where the source has no type discriminator. + /// + /// `TransmuteInfo` carries the source and destination types used to compute type + /// discriminators. These may differ from the original operand layouts after transparent + /// wrapper or nullable normalization. + fn ptrauth_resign_transmuted_fn_ptr( + &mut self, + bx: &mut Bx, + val: Bx::Value, + info: TransmuteInfo<'tcx>, + ) -> Bx::Value { + // Resigning can only happen in the context of function pointer type discrimination. + assert!(self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination()); + + let tcx = bx.tcx(); + + let src_disc = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, info.src_ty).unwrap_or(0); + let dst_disc = ptrauth_compute_fn_ptr_type_discriminator_for(tcx, info.dst_ty).unwrap_or(0); + + if src_disc == dst_disc { + return val; + } + + let key = self.cx.tcx().sess.pointer_authentication_fn_ptr_key().unwrap() as u32; + bx.ptrauth_resign(val, key, src_disc.into(), key, dst_disc.into()) + } + + /// Resigns a nullable function pointer. + /// + /// `llvm.ptrauth.resign` performs an authenticate-and-resign operation on an already signed + /// pointer. A null function pointer carries no authentication signature, so it must bypass the + /// intrinsic and remain null. + fn ptrauth_resign_transmuted_nullable_fn_ptr( + &mut self, + bx: &mut Bx, + ptr: Bx::Value, + info: TransmuteInfo<'tcx>, + ) -> Bx::Value { + let ptrtoint = bx.ptrtoint(ptr, bx.type_isize()); + + // Fast path for compile time known null value. + if bx.const_to_opt_u128(ptrtoint, false) == Some(0) { + return bx.const_null(bx.type_ptr()); + } + + let pointer_align = bx.tcx().data_layout.pointer_align().abi; + let pointer_size = bx.tcx().data_layout.pointer_size(); + let result = bx.alloca(pointer_size, pointer_align); + + let null_bb = bx.append_sibling_block("ptrauth.null"); + let resign_bb = bx.append_sibling_block("ptrauth.resign"); + let end_bb = bx.append_sibling_block("ptrauth.end"); + + let is_null = bx.icmp(IntPredicate::IntEQ, ptrtoint, bx.const_usize(0)); + bx.cond_br(is_null, null_bb, resign_bb); + + bx.switch_to_block(null_bb); + bx.store(bx.const_null(bx.type_ptr()), result, pointer_align); + bx.br(end_bb); + + bx.switch_to_block(resign_bb); + let resigned = self.ptrauth_resign_transmuted_fn_ptr(bx, ptr, info); + bx.store(resigned, result, pointer_align); + bx.br(end_bb); + + bx.switch_to_block(end_bb); + bx.load(bx.type_ptr(), result, pointer_align) + } + + /// Returns the underlying function pointer type represented by `layout`. + /// + /// Transparent wrappers and `Option` are peeled until either a function pointer is reached + /// or a non-wrapper type is encountered. + /// + /// The returned boolean indicates whether an `Option` wrapper was seen, meaning the + /// function pointer uses a nullable representation. + fn ptrauth_canonicalize_fn_ptr_layout( + &self, + mut layout: TyAndLayout<'tcx>, + ) -> Option<(Ty<'tcx>, bool)> { + let mut nullable = false; + loop { + match layout.ty.kind() { + ty::FnPtr(..) | ty::FnDef(..) => { + return Some((layout.ty, nullable)); + } + + ty::Adt(def, _) if def.repr().transparent() => { + let Some((_, field_layout)) = layout.non_1zst_field(self.cx) else { + // Every field is a ZST - early exit. + return None; + }; + layout = field_layout; + } + + ty::Adt(def, args) + if self.cx.tcx().lang_items().option_type() == Some(def.did()) + // Only nullable-peel if `Option` genuinely niche-optimized to a single + // scalar, rejecting `Option>`. + && matches!(layout.backend_repr, abi::BackendRepr::Scalar(_)) => + { + nullable = true; + layout = self.cx.layout_of(args.type_at(0)); + } + + _ => return None, + } + } + } + + /// Applies pointer-authentication resign for a function pointer transmute and stores the + /// resigned value into the destination place. + /// + /// The source operand may be an SSA immediate, or may already reside in memory (its caller in + /// `ptrauth_codegen_transmute_place` receives the source as a place, not a register). + fn ptrauth_resign_fn_ptr( + &mut self, + bx: &mut Bx, + src: OperandRef<'tcx, Bx::Value>, + dst: PlaceRef<'tcx, Bx::Value>, + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx>, + nullable: bool, + ) { + let val = match src.val { + OperandValue::Immediate(v) => v, + + OperandValue::Ref(place) => bx.load_operand(place.with_type(src.layout)).immediate(), + + _ => { + bug!( + "unexpected operand representation for function pointer transmute: {:?}", + src.val + ); + } + }; + + let info = TransmuteInfo { src_ty, dst_ty }; + + let val = if nullable { + self.ptrauth_resign_transmuted_nullable_fn_ptr(bx, val, info) + } else { + self.ptrauth_resign_transmuted_fn_ptr(bx, val, info) + }; + + OperandRef { val: OperandValue::Immediate(val), layout: dst.layout, move_annotation: None } + .store_with_annotation(bx, dst); + } + + /// Applies pointer-authentication type discriminator correction when transmuting into a + /// memory-backed place. + /// + /// Resigning only happens when the transmute is directly between (or through a transparent + /// wrapper or `Option` around) extern "C" function pointer types. A transmute of an aggregate + /// that contains a function pointer field is deliberately lowered as an ordinary copy, with + /// no resigning. This matches how Clang lowers the analogous C code: a struct-level pointer + /// cast or struct copy does not re-sign a nested function-pointer field, and a real type + /// mismatch is instead caught by authentication failing at the point the pointer is actually + /// called. + fn ptrauth_codegen_transmute_place( + &mut self, + bx: &mut Bx, + src: OperandRef<'tcx, Bx::Value>, + dst: PlaceRef<'tcx, Bx::Value>, + ) { + if let (Some((src_ty, src_nullable)), Some((dst_ty, dst_nullable))) = ( + self.ptrauth_canonicalize_fn_ptr_layout(src.layout), + self.ptrauth_canonicalize_fn_ptr_layout(dst.layout), + ) { + self.ptrauth_resign_fn_ptr(bx, src, dst, src_ty, dst_ty, src_nullable || dst_nullable); + return; + } + + src.store_with_annotation(bx, dst.val.with_type(src.layout)); + } + fn is_entirely_uninit_const(&self, operand: &mir::Operand<'tcx>) -> bool { let mir::Operand::Constant(const_op) = operand else { return false }; self.eval_mir_constant(const_op).all_bytes_uninit(self.cx.tcx()) @@ -186,8 +426,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Cast( mir::CastKind::Transmute | mir::CastKind::Subtype, ref operand, - _ty, + ty, ) => { + if self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + let src_ty = operand.ty(self.mir, self.cx.tcx()); + let dst_ty = self.monomorphize(ty); + + if src_ty.is_fn_ptr() || dst_ty.is_fn_ptr() { + let op = self.codegen_operand(bx, operand); + let cast = bx.cx().layout_of(dst_ty); + + let val = self.ptrauth_codegen_transmute_operand(bx, op, cast); + + OperandRef { val, layout: cast, move_annotation: None } + .store_with_annotation(bx, dest); + + return; + } + } + let src = self.codegen_operand(bx, operand); self.codegen_transmute(bx, src, dest); } @@ -324,7 +581,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Since in this path we have a place anyway, we can store or copy to it, // making sure we use the destination place's alignment even if the // source would normally have a higher one. - src.store_with_annotation(bx, dst.val.with_type(src.layout)); + + if self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + self.ptrauth_codegen_transmute_place(bx, src, dst); + } else { + src.store_with_annotation(bx, dst.val.with_type(src.layout)); + } } } @@ -338,6 +600,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operand: OperandRef<'tcx, Bx::Value>, cast: TyAndLayout<'tcx>, ) -> OperandValue { + debug!( + "codegen_transmute_operand\t + from_ty={:?} to_ty={:?} from_layout={:?} to_layout={:?} is fnptr=({}, {})", + operand.layout.ty, + cast.ty, + operand.layout.backend_repr, + cast.backend_repr, + operand.layout.ty.is_fn_ptr(), + cast.ty.is_fn_ptr() + ); if let abi::BackendRepr::Memory { .. } = cast.backend_repr && !cast.is_zst() { @@ -523,12 +795,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args.no_bound_vars().unwrap(), ) .unwrap(); - OperandValue::Immediate( - bx.get_fn_addr( - instance, - bx.sess().pointer_authentication_functions(), - ), + + let schema = if bx.sess().pointer_authentication_fn_ptr_type_discrimination() { + ptrauth_clone_discriminated_schema_for( + bx.tcx(), + bx.sess().pointer_authentication_functions(), + operand.layout.ty, ) + } else { + bx.sess().pointer_authentication_functions().clone() + }; + + OperandValue::Immediate(bx.get_fn_addr(instance, schema)) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -542,10 +820,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ty::ClosureKind::FnOnce, ); + assert!( + !matches!( + bx.cx().tcx().fn_sig(instance.def_id()).skip_binder().abi(), + ExternAbi::C { .. } | ExternAbi::System { .. } + ) + ); OperandValue::Immediate( + // A closure coerced to a function pointer retains the Rust + // ABI. Pointer authentication only applies to extern + // "C"/System ABI function pointer, hence pass None to + // `get_fn_addr`. bx.cx().get_fn_addr( instance, - bx.sess().pointer_authentication_functions(), + /* ptrauth_schema */ None, ), ) } @@ -622,7 +910,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }) } mir::CastKind::Transmute | mir::CastKind::BoxDerefTransmute | mir::CastKind::Subtype => { - self.codegen_transmute_operand(bx, operand, cast) + if self.cx.tcx().sess.pointer_authentication_fn_ptr_type_discrimination() { + self.ptrauth_codegen_transmute_operand(bx, operand, cast) + } else { + self.codegen_transmute_operand(bx, operand, cast) + } } }; OperandRef { val, layout: cast, move_annotation: None } @@ -767,8 +1059,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def: ty::InstanceKind::Shim(ty::ShimKind::ThreadLocal(def_id)), args: ty::GenericArgs::empty(), }; - let fn_ptr = - bx.get_fn_addr(instance, bx.sess().pointer_authentication_functions()); + // needs_thread_local_shim implies Windows/MSVC, for which pointer + // authentication is not yet supported. + assert!(!self.cx.tcx().sess.pointer_authentication()); + let fn_ptr = bx.get_fn_addr(instance, /* ptrauth_schema */ None); let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty()); let fn_ty = bx.fn_decl_backend_type(fn_abi); let fn_attrs = if bx.tcx().def_kind(instance.def_id()).has_codegen_attrs() { From 12191c98cf65d9461b40b5f1bddc252bb4ddf7a0 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Mon, 21 Sep 2026 10:06:06 +0000 Subject: [PATCH 10/11] [PAC] Correctly handle raw pointers in memory-based path Recognize raw pointers as 0-discriminated. Also, when canonicalizing, move away from hard coded Option check to a generic variant with 2 fields and correct niche. --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 73 +++++++++++++------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 95517eb3a7097..f95b4287a9add 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -103,6 +103,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { true } + /// Resolve/canonicalize the src and dst types, treating a side that doesn't canonicalize to a + /// function pointer as a zero-discriminator raw value (matching Clang's treatment of raw + /// pointers in the analogous C code). + fn ptrauth_transmute_resign_info( + &self, + src_layout: TyAndLayout<'tcx>, + dst_layout: TyAndLayout<'tcx>, + ) -> Option<(TransmuteInfo<'tcx>, bool)> { + let src_semantic = self.ptrauth_canonicalize_fn_ptr_layout(src_layout); + let dst_semantic = self.ptrauth_canonicalize_fn_ptr_layout(dst_layout); + + if src_semantic.is_none() && dst_semantic.is_none() { + return None; + } + + let info = TransmuteInfo { + src_ty: src_semantic.map_or(src_layout.ty, |(ty, _)| ty), + dst_ty: dst_semantic.map_or(dst_layout.ty, |(ty, _)| ty), + }; + let nullable = src_semantic.is_some_and(|(_, n)| n) || dst_semantic.is_some_and(|(_, n)| n); + Some((info, nullable)) + } + /// Lowers a transmute of an SSA operand while preserving pointer authentication semantics. /// When the source and destination are both, or transparently wrap, function pointer types /// with different type discriminators, the resulting pointer is re-signed. @@ -121,28 +144,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return val; }; - let src_semantic = self.ptrauth_canonicalize_fn_ptr_layout(operand.layout); - let dst_semantic = self.ptrauth_canonicalize_fn_ptr_layout(cast); - - // Neither side is a function pointer - early return. - if src_semantic.is_none() && dst_semantic.is_none() { + let Some((info, nullable)) = self.ptrauth_transmute_resign_info(operand.layout, cast) + else { return val; - } - - let info = TransmuteInfo { - src_ty: src_semantic.map_or(operand.layout.ty, |(ty, _)| ty), - dst_ty: dst_semantic.map_or(cast.ty, |(ty, _)| ty), }; - let nullable = src_semantic.is_some_and(|(_, n)| n) || dst_semantic.is_some_and(|(_, n)| n); - let ptr = if nullable { self.ptrauth_resign_transmuted_nullable_fn_ptr(bx, ptr, info) } else { self.ptrauth_resign_transmuted_fn_ptr(bx, ptr, info) }; - OperandValue::Immediate(ptr) + return OperandValue::Immediate(ptr); } /// Applies pointer-authentication type discriminator correction for a function pointer value @@ -253,13 +266,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } ty::Adt(def, args) - if self.cx.tcx().lang_items().option_type() == Some(def.did()) - // Only nullable-peel if `Option` genuinely niche-optimized to a single - // scalar, rejecting `Option>`. - && matches!(layout.backend_repr, abi::BackendRepr::Scalar(_)) => + if def.is_enum() + && def.variants().len() == 2 + && matches!(layout.backend_repr, abi::BackendRepr::Scalar(_)) + && let abi::Variants::Multiple { + tag_encoding: abi::TagEncoding::Niche { untagged_variant, .. }, + .. + } = layout.variants => { + let variant = def.variant(untagged_variant); + let [field] = &variant.fields.raw[..] else { + return None; + }; nullable = true; - layout = self.cx.layout_of(args.type_at(0)); + let field_ty = self.cx.tcx().normalize_erasing_regions( + self.cx.typing_env(), + field.ty(self.cx.tcx(), args), + ); + layout = self.cx.layout_of(field_ty); } _ => return None, @@ -322,15 +346,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { src: OperandRef<'tcx, Bx::Value>, dst: PlaceRef<'tcx, Bx::Value>, ) { - if let (Some((src_ty, src_nullable)), Some((dst_ty, dst_nullable))) = ( - self.ptrauth_canonicalize_fn_ptr_layout(src.layout), - self.ptrauth_canonicalize_fn_ptr_layout(dst.layout), - ) { - self.ptrauth_resign_fn_ptr(bx, src, dst, src_ty, dst_ty, src_nullable || dst_nullable); + let Some((info, nullable)) = self.ptrauth_transmute_resign_info(src.layout, dst.layout) + else { + src.store_with_annotation(bx, dst.val.with_type(src.layout)); return; - } - - src.store_with_annotation(bx, dst.val.with_type(src.layout)); + }; + self.ptrauth_resign_fn_ptr(bx, src, dst, info.src_ty, info.dst_ty, nullable); } fn is_entirely_uninit_const(&self, operand: &mir::Operand<'tcx>) -> bool { From fc5d101b1161d5c022523c17ea2c838f52076a54 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 13:26:56 +0000 Subject: [PATCH 11/11] [PAC] Propagate function pointer type discrimination through `get_fn_addr` call sites Fill in function pointer type discriminators logic across remaining `get_fn_addr` call sites and explicitly avoid applying it where discrimination is not meaningful. Some uses of `get_fn_addr` are intentionally left unsigned, including the EH personality function, entry wrappers, and compiler-generated Rust ABI shims. --- compiler/rustc_codegen_llvm/src/context.rs | 14 +++++- compiler/rustc_codegen_ssa/src/base.rs | 28 +++++++++-- compiler/rustc_codegen_ssa/src/common.rs | 20 ++++++-- compiler/rustc_codegen_ssa/src/mir/block.rs | 54 +++++++++++++++------ 4 files changed, 91 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 2e0db574e8d62..67417b9b9eef2 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1047,6 +1047,18 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { + // We intentionally do not apply pointer authentication (and/or function type + // discriminators to the EH personality function). + // + // Although `get_fn_addr` normally produces a signed function pointer for + // externally-callable functions, the EH personality is not an indirect call + // target in the SSA sense. Instead, it is a compile-time constant attached to + // the Function object (via LLVM's `setPersonalityFn`) and consumed only by + // exception handling metadata generation (landing pads / unwind tables). + // LLVM never loads or invokes the personality via a function pointer value; + // it is not part of the program's call graph or data flow. + // It's backend's responsibility to apply ABI-specific personality signing + // when emitting the pointer in the object file. Some(def_id) if name.is_none() => self.get_fn_addr( ty::Instance::expect_resolve( tcx, @@ -1055,7 +1067,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { ty::List::empty(), DUMMY_SP, ), - tcx.sess.pointer_authentication_functions(), + None, ), _ => { let name = name.unwrap_or("rust_eh_personality"); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index e054394ccade0..d3a3c6b024754 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -4,7 +4,7 @@ use std::time::{Duration, Instant}; use std::{cmp, iter}; use itertools::Itertools; -use rustc_abi::FIRST_VARIANT; +use rustc_abi::{ExternAbi, FIRST_VARIANT}; use rustc_ast::expand::allocator::{ ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorMethodInput, AllocatorTy, @@ -515,8 +515,18 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // We want to create the wrapper only when the codegen unit is the primary one return None; } - - let main_llfn = cx.get_fn_addr(instance, cx.sess().pointer_authentication_functions()); + // No function pointer signing / type discriminator is needed here. Although `get_fn_addr` is + // used to obtain function pointers, both the user's `main` and `LangItem::Start` use the Rust + // ABI (currently pointer authentication is only supported for C/System ABI). The same applies + // to the logic in `create_entry_fn` further below. + assert!( + !matches!( + cx.tcx().fn_sig(main_def_id).skip_binder().abi(), + ExternAbi::C { .. } | ExternAbi::System { .. } + ), + "entry wrapper assumes Rust ABI" + ); + let main_llfn = cx.get_fn_addr(instance, /* pointer_auth_schema */ None); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); @@ -577,8 +587,16 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx.tcx().mk_args(&[main_ret_ty.into()]), DUMMY_SP, ); - let start_fn = - cx.get_fn_addr(start_instance, cx.sess().pointer_authentication_functions()); + // Start instance doesn't require signing, as it uses Rust ABI, hence pass `None` to + // `get_fn_addr`. + assert!( + !matches!( + cx.tcx().fn_sig(start_instance.def_id()).skip_binder().abi(), + ExternAbi::C { .. } | ExternAbi::System { .. } + ), + "LangItem::Start unexpectedly uses the C/System ABI", + ); + let start_fn = cx.get_fn_addr(start_instance, None); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 4b54235cf71bc..befa9214ce1c1 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -4,6 +4,7 @@ use rustc_crate_store::{DllCallingConvention, DllImport, DllImportSymbolType}; use rustc_hir::attrs::PeImportNameType; use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; +use rustc_middle::ptrauth::ptrauth_clone_discriminated_schema_for; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Instance, ScalarInt, TyCtxt}; use rustc_span::{Span, bug, span_bug}; @@ -117,11 +118,20 @@ pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let tcx = bx.tcx(); let def_id = tcx.require_lang_item(li, span); let instance = ty::Instance::mono(tcx, def_id); - ( - bx.fn_abi_of_instance(instance, ty::List::empty()), - bx.get_fn_addr(instance, tcx.sess.pointer_authentication_functions()), - instance, - ) + + let schema = if bx.sess().pointer_authentication_fn_ptr_type_discrimination() { + // It is unlikely that any of LangItem will follow the extern C/System ABI, but it future + // proofs the implementation. + ptrauth_clone_discriminated_schema_for( + bx.tcx(), + bx.sess().pointer_authentication_functions(), + instance, + ) + } else { + bx.sess().pointer_authentication_functions().clone() + }; + + (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance, schema), instance) } pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f99009a0f4243..6dfe50b28cefe 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -13,6 +13,7 @@ use rustc_hir::attrs::lang_items::LangItem; use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, Scalar}; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; +use rustc_middle::ptrauth::ptrauth_clone_discriminated_schema_for; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt}; @@ -712,12 +713,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { virtual_drop, ) } - _ => ( - false, - bx.get_fn_addr(drop_fn, bx.sess().pointer_authentication_functions()), - bx.fn_abi_of_instance(drop_fn, ty::List::empty()), - drop_fn, - ), + _ => { + let schema = if bx.sess().pointer_authentication_fn_ptr_type_discrimination() { + ptrauth_clone_discriminated_schema_for( + bx.tcx(), + bx.sess().pointer_authentication_functions(), + drop_fn, + ) + } else { + bx.sess().pointer_authentication_functions().clone() + }; + ( + false, + bx.get_fn_addr(drop_fn, schema), + bx.fn_abi_of_instance(drop_fn, ty::List::empty()), + drop_fn, + ) + } }; // We generate a null check for the drop_fn. This saves a bunch of relocations being @@ -1133,14 +1145,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { generic_args.no_bound_vars().unwrap(), ) .unwrap(); + let schema = + if bx.sess().pointer_authentication_fn_ptr_type_discrimination() { + ptrauth_clone_discriminated_schema_for( + bx.tcx(), + bx.sess().pointer_authentication_functions(), + instance, + ) + } else { + bx.sess().pointer_authentication_functions().clone() + }; - ( - None, - Some(bx.get_fn_addr( - instance, - bx.sess().pointer_authentication_functions(), - )), - ) + (None, Some(bx.get_fn_addr(instance, schema))) } _ => (Some(instance), None), } @@ -1461,7 +1477,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let fn_ptr = match (instance, llfn) { (Some(instance), None) => { - bx.get_fn_addr(instance, bx.sess().pointer_authentication_functions()) + let schema = if bx.sess().pointer_authentication_fn_ptr_type_discrimination() { + ptrauth_clone_discriminated_schema_for( + bx.tcx(), + bx.sess().pointer_authentication_functions(), + instance, + ) + } else { + bx.sess().pointer_authentication_functions().clone() + }; + + bx.get_fn_addr(instance, schema) } (_, Some(llfn)) => llfn, _ => span_bug!(fn_span, "no instance or llfn for call"),