From 4c1b0acc0e713eb4ae922c0b29aad755fb48bb25 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 11:13:30 +0000 Subject: [PATCH 1/4] [PAC] Introduce function pointer type encoder and hashing function This patch implements Rust's equivalent of Clang's function pointer type discriminator computation used in pointer authentication. Compatibility with Clang is a primary goal. The discriminator produced for a given external "C" function type must match the value computed by Clang so that function pointers can be exchanged safely between Rust and C code while preserving pointer authentication semantics. The implementation mirrors Clang's behavior in `ASTContext::encodeTypeForFunctionPointerAuth`, ensuring that identical C-compatible function types produce identical discriminators. See: . --- compiler/rustc_middle/src/lib.rs | 1 + .../rustc_middle/src/ptrauth/discriminator.rs | 493 ++++++++++++++++++ .../rustc_middle/src/ptrauth/llvm_siphash.rs | 142 +++++ compiler/rustc_middle/src/ptrauth/mod.rs | 7 + 4 files changed, 643 insertions(+) create mode 100644 compiler/rustc_middle/src/ptrauth/discriminator.rs create mode 100644 compiler/rustc_middle/src/ptrauth/llvm_siphash.rs create mode 100644 compiler/rustc_middle/src/ptrauth/mod.rs diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 69c2e099080c9..9798130e69ef0 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -80,6 +80,7 @@ pub mod metadata; pub mod middle; pub mod mir; pub mod mono; +pub mod ptrauth; pub mod queries; pub mod query; pub mod thir; diff --git a/compiler/rustc_middle/src/ptrauth/discriminator.rs b/compiler/rustc_middle/src/ptrauth/discriminator.rs new file mode 100644 index 0000000000000..76d7fe9b1f0ea --- /dev/null +++ b/compiler/rustc_middle/src/ptrauth/discriminator.rs @@ -0,0 +1,493 @@ +/*! +Function pointer type discrimination for pointer authentication. + +This module implements Rust's equivalent of Clang's function pointer type +discriminator computation used in pointer authentication. + +Compatibility with Clang is a primary goal. The discriminator produced for a +given external "C" function type must match the value computed by Clang so that +function pointers can be exchanged safely between Rust and C code while +preserving pointer authentication semantics. + +The implementation mirrors Clang's behavior in +`ASTContext::encodeTypeForFunctionPointerAuth`, ensuring that identical +C-compatible function types produce identical discriminators. See: +. + +## Overview + +The computation is structured into three conceptual stages: + +### 1. Type normalization and lowering + Rust types are converted into a language-independent representation + (`ClangDiscTy`) that mirrors the type categories used by Clang when computing + function pointer discriminators. This includes canonicalization such as + treating all pointer-like types uniformly and mapping Rust constructs onto + their closest C equivalents. + 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)` + or `(f64, f64)` as complex numbers), this implementation leaves such + representation choices to users and does not provide dedicated `_Complex` + 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`. + +## Module structure + +- High-level API + - `FnPtrDiscriminatorSource` + - `compute_fn_ptr_type_discriminator_for` + - `clone_discriminated_ptrauth_schema_for` + +- Low-level API + - `FnPtrTypeDiscriminatorInput` + - `compute_fn_ptr_type_discriminator` + +- Signature extraction + - `extract_fn_ptr_type` + +- Clang-compatible type model + - `ClangDiscTy` + - `canonicalize_c_type` + - `to_clang_disc_ty` + +- Encoding + - `PtrauthEncoder` + - `encode_ty` + +## Compatibility requirements + +Any changes to the encoding or hashing logic should be validated against Clang's +discriminator computation. Divergence from Clang will result in incompatible +pointer authentication values across language boundaries. + +This implementation intentionally approximates Clang's behavior for extern "C" +function types only. It does NOT attempt to model full type system rules. +*/ + +use rustc_abi::ExternAbi; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, Unnormalized}; +use rustc_session::PointerAuthSchema; +use rustc_span::sym; + +use crate::ptrauth::llvm_siphash::llvm_pointer_auth_stable_siphash; + +/// Types that can serve as a source for function pointer type discrimination. +/// +/// This trait abstracts over the different compiler representations from which +/// a function signature can be obtained. Implementations construct the +/// canonical `FnPtrTypeDiscriminatorInput` consumed by the discriminator +/// computation. +/// +/// This is intended primarily for ergonomic use at call sites, allowing code +/// to compute discriminators directly from an `Instance`, `Ty`, or `FnSig` +/// without manually constructing the intermediate representation. +pub trait FnPtrDiscriminatorSource<'tcx>: Sized { + fn discriminator_input(self, tcx: TyCtxt<'tcx>) -> Option>; +} + +/// Enables discriminator computation directly from Rust function types. +/// +/// Accepts both: +/// - `FnPtr`: actual function pointer types +/// - `FnDef`: function items +/// +/// FnDef is only accepted for convenience; the discriminator is still computed +/// from the instantiated function signature. +impl<'tcx> FnPtrDiscriminatorSource<'tcx> for Ty<'tcx> { + fn discriminator_input(self, tcx: TyCtxt<'tcx>) -> Option> { + let ty = extract_fn_ptr_type(tcx, self)?; + + match ty.kind() { + ty::FnPtr(sig, header) => { + let sig = sig.skip_binder(); + Some(FnPtrTypeDiscriminatorInput::from_sig_tys(sig, header)) + } + + ty::FnDef(def_id, args) => { + let sig = tcx.fn_sig(*def_id).instantiate(tcx, args.skip_binder()).skip_binder(); + + Some(FnPtrTypeDiscriminatorInput::from_sig(sig)) + } + + _ => None, + } + } +} +/// Enables discriminator computation directly from monomorphized function +/// instances. +/// +/// The instance's signature is instantiated using its generic arguments and +/// 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)) + } +} +/// Enables discriminator computation directly from instantiated function +/// signatures. +/// +/// The signature is assumed to already be instantiated and normalized. +impl<'tcx> FnPtrDiscriminatorSource<'tcx> for ty::FnSig<'tcx> { + fn discriminator_input(self, _: TyCtxt<'tcx>) -> Option> { + Some(FnPtrTypeDiscriminatorInput::from_sig(self)) + } +} + +/// Computes the function pointer type discriminator directly from a supported +/// source. +/// +/// This is a convenience wrapper around +/// `FnPtrDiscriminatorSource::discriminator_input` and +/// `compute_fn_ptr_type_discriminator`. +/// +/// Returns `None` if the supplied source does not represent a function pointer +/// type (for example, a non-function `Ty`). +pub fn compute_fn_ptr_type_discriminator_for<'tcx, S>(tcx: TyCtxt<'tcx>, source: S) -> Option +where + S: FnPtrDiscriminatorSource<'tcx>, +{ + let input = source.discriminator_input(tcx)?; + Some(compute_fn_ptr_type_discriminator(tcx, &input)) +} + +/// Clones a pointer authentication schema and updates its constant +/// discriminator. +/// +/// If `schema` is `Some`, the function computes a function pointer type +/// discriminator from `source` and stores it in the cloned schema's +/// `constant_discriminator` field. +/// +/// If no discriminator can be computed (for example, because `source` does not +/// represent a function pointer type), the schema is returned unchanged. +/// +/// This is intended as a convenience helper for code generation sites that need +/// to attach function pointer type discrimination to a generic schema before +/// calling `get_fn_addr`. +pub fn clone_discriminated_ptrauth_schema_for<'tcx, S>( + tcx: TyCtxt<'tcx>, + mut schema: Option, + source: S, +) -> Option +where + S: FnPtrDiscriminatorSource<'tcx>, +{ + if let Some(ref mut s) = schema { + if let Some(disc) = compute_fn_ptr_type_discriminator_for(tcx, source) { + s.constant_discriminator = disc; + } + } + + schema +} + +/// Canonical representation of a function signature used for pointer +/// authentication discriminator generation. +#[derive(Debug)] +pub struct FnPtrTypeDiscriminatorInput<'tcx> { + inputs: &'tcx [Ty<'tcx>], + output: Ty<'tcx>, + abi: ExternAbi, + c_variadic: bool, +} + +impl<'tcx> FnPtrTypeDiscriminatorInput<'tcx> { + fn from_sig(sig: ty::FnSig<'tcx>) -> Self { + FnPtrTypeDiscriminatorInput { + inputs: sig.inputs(), + output: sig.output(), + abi: sig.abi(), + c_variadic: sig.c_variadic(), + } + } + + fn from_sig_tys(sig: ty::FnSigTys>, header: &ty::FnHeader>) -> Self { + FnPtrTypeDiscriminatorInput { + inputs: sig.inputs(), + output: sig.output(), + abi: header.abi(), + c_variadic: header.c_variadic(), + } + } +} + +/// Unwraps optional function pointers and normalizes the type. +/// +/// Only `Option` is supported for nullability modeling, matching C ABI +/// null pointer conventions. +fn extract_fn_ptr_type<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Option> { + ty = tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), Unnormalized::new(ty)); + + loop { + match ty.kind() { + ty::Adt(def, args) if tcx.is_diagnostic_item(sym::Option, def.did()) => { + ty = args.type_at(0); + continue; + } + + ty::FnPtr(..) | ty::FnDef(..) => { + return Some(ty); + } + + _ => return None, + } + } +} + +/// 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>( + tcx: TyCtxt<'tcx>, + input: &FnPtrTypeDiscriminatorInput<'tcx>, +) -> u16 { + if !matches!(input.abi, ExternAbi::C { .. } | ExternAbi::System { .. }) { + return 0; + } + + let mut enc = PtrauthEncoder::new(); + enc.push(b'F'); + + encode_ty(&mut enc, tcx, input.output); + + for &arg in input.inputs { + encode_ty(&mut enc, tcx, arg); + } + + if input.c_variadic { + enc.push(b'z'); + } + + enc.push(b'E'); + + let hash = enc.finish(); + + hash.into() +} + +// Clang disc type. +#[derive(Debug)] +enum ClangDiscTy<'tcx> { + Int, + Float(&'tcx ty::FloatTy), + Bool, + Char, + + // Pointer-like types in the C ABI sense. + // This includes: + // - raw pointers (`*const T`, `*mut T`) + // - Rust references (`&T`, `&mut T`) + // - function pointers + // All collapse to a single Clang-compatible 'P' node. + Pointer, + + Array { elem: Ty<'tcx> }, + + // FIXME(jchlands) Decide if to support Complex types in future. Clang has + // dedicated node for this `Type::Complex`, Rust does not. So we could match + // against a Tuple(FP_TYPE, FP_TYPE). + // Complex(Ty<'tcx>), + Vector { bytes: u64 }, + + EnumLikeInt, + AdtName(String), + Opaque, + Void, +} + +// Canonicalize Option-wrapped pointer types used to model C nullable pointers. +// +// 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. +// +// 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, + _ => {} + } + } + + ty +} + +/// Lowers a Rust type into a Clang-compatible discriminator type. +/// +/// This is not a full semantic translation of Rust types. It is a lossy mapping +/// that intentionally matches Clang's function pointer authentication encoding +/// rules. +/// This is not a full semantic translation of Rust types. It is a lossy mapping +/// that intentionally matches Clang's function pointer authentication encoding +/// rules where Rust has a direct language-level equivalent. +/// +/// In particular C `_Complex`, without a canonical Rust equivalent, is not +/// recognized. This avoids introducing heuristics for user-defined +/// representations that may vary across codebases. +/// +/// Important invariants: +/// - All pointer-like types (Rust refs, raw pointers, fn pointers) collapse to +/// `Pointer`. +/// - Struct/union types are encoded using name only, not layout. +/// - Enums are treated as integers. +/// - SIMD types are encoded only by total byte size (no lane semantics). +/// - No attempt is made to recognize user-defined representations of C +/// `_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); + match ty.kind() { + // C void / Rust () + ty::Tuple(list) if list.is_empty() => ClangDiscTy::Void, + + // scalars + ty::Bool => ClangDiscTy::Bool, + ty::Char => ClangDiscTy::Char, + + ty::Int(_) | ty::Uint(_) => ClangDiscTy::Int, + ty::Float(f) => ClangDiscTy::Float(f), + + // everything pointer-like collapses + ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) | ty::Dynamic(..) | ty::Slice(_) | ty::Str => { + ClangDiscTy::Pointer + } + + // arrays ignore size + ty::Array(elem, _) => ClangDiscTy::Array { elem: *elem }, + + // enums to integer collapse + ty::Adt(def, _) if def.is_enum() => ClangDiscTy::EnumLikeInt, + // simd vectors + ty::Adt(def, args) if def.repr().simd() => { + // Clang encodes SIMD vectors by their total size + let input = ty::PseudoCanonicalInput { + typing_env: ty::TypingEnv::fully_monomorphized(), + value: ty, + }; + + let Ok(layout) = tcx.layout_of(input) else { + tcx.dcx().delayed_bug("could not compute SIMD layout"); + return ClangDiscTy::Opaque; + }; + + let bytes = layout.size.bytes(); + + ClangDiscTy::Vector { bytes } + } + // structs/unions to name-based identity + ty::Adt(def, _) => { + let name = tcx.item_name(def.did()).to_string(); + ClangDiscTy::AdtName(name) + } + + ty::Foreign(_) => ClangDiscTy::Opaque, + + _ => ClangDiscTy::Opaque, + } +} + +// Encoder +struct PtrauthEncoder { + buf: Vec, +} + +impl PtrauthEncoder { + fn new() -> Self { + Self { buf: Vec::new() } + } + + fn push(&mut self, b: u8) { + self.buf.push(b); + } + + fn push_str(&mut self, s: &str) { + self.buf.extend_from_slice(s.as_bytes()); + } + + fn finish(&self) -> u16 { + llvm_pointer_auth_stable_siphash(&self.buf) + } +} + +/// Encodes a ClangDiscTy into the discriminator byte stream. +/// +/// This format is intended to be bit-for-bit compatible with Clang's +/// `encodeTypeForFunctionPointerAuth`. +fn encode_ty<'tcx>(enc: &mut PtrauthEncoder, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { + let cty = to_clang_disc_ty(tcx, ty); + + match cty { + // scalars + ClangDiscTy::Bool | ClangDiscTy::Char | ClangDiscTy::Int => enc.push(b'i'), + + ClangDiscTy::Float(f) => match f.bit_width() { + 16 => enc.push_str("Dh"), + 32 => enc.push(b'f'), + 64 => enc.push(b'd'), + 128 => enc.push(b'g'), + _ => enc.push(b'?'), + }, + + ClangDiscTy::Void => enc.push(b'v'), + + // pointer boundary (NO RECURSION) + ClangDiscTy::Pointer => enc.push(b'P'), + + // arrays ignore size + ClangDiscTy::Array { elem } => { + enc.push(b'A'); + encode_ty(enc, tcx, elem); + } + + // enums collapse + ClangDiscTy::EnumLikeInt => enc.push(b'i'), + + // ADT identity + ClangDiscTy::AdtName(name) => { + enc.push_str(&name.len().to_string()); + enc.push_str(&name); + } + + ClangDiscTy::Opaque => enc.push(b'?'), + + ClangDiscTy::Vector { bytes } => { + enc.push_str("Dv"); + enc.push_str(&bytes.to_string()); + } + } +} diff --git a/compiler/rustc_middle/src/ptrauth/llvm_siphash.rs b/compiler/rustc_middle/src/ptrauth/llvm_siphash.rs new file mode 100644 index 0000000000000..68aeac11a0429 --- /dev/null +++ b/compiler/rustc_middle/src/ptrauth/llvm_siphash.rs @@ -0,0 +1,142 @@ +// LLVM SipHash-2-4 +pub fn llvm_pointer_auth_stable_siphash(data: &[u8]) -> u16 { + let raw = llvm_siphash_2_4_64(data); + + ((raw % 0xFFFF) + 1) as u16 +} + +const SIPHASH_KEY: [u8; 16] = [ + 0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79, 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4, +]; + +#[inline(always)] +fn rotl(x: u64, b: u32) -> u64 { + (x << b) | (x >> (64 - b)) +} + +#[inline(always)] +fn sipround(v0: &mut u64, v1: &mut u64, v2: &mut u64, v3: &mut u64) { + *v0 = v0.wrapping_add(*v1); + *v1 = rotl(*v1, 13); + *v1 ^= *v0; + *v0 = rotl(*v0, 32); + + *v2 = v2.wrapping_add(*v3); + *v3 = rotl(*v3, 16); + *v3 ^= *v2; + + *v0 = v0.wrapping_add(*v3); + *v3 = rotl(*v3, 21); + *v3 ^= *v0; + + *v2 = v2.wrapping_add(*v1); + *v1 = rotl(*v1, 17); + *v1 ^= *v2; + *v2 = rotl(*v2, 32); +} + +fn u64_from_le(bytes: &[u8]) -> u64 { + u64::from_le_bytes(bytes.try_into().unwrap()) +} + +fn load_u64_partial(bytes: &[u8]) -> u64 { + let mut b = 0u64; + + match bytes.len() { + 7 => { + b |= (bytes[6] as u64) << 48; + b |= (bytes[5] as u64) << 40; + b |= (bytes[4] as u64) << 32; + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 6 => { + b |= (bytes[5] as u64) << 40; + b |= (bytes[4] as u64) << 32; + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 5 => { + b |= (bytes[4] as u64) << 32; + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 4 => { + b |= (bytes[3] as u64) << 24; + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 3 => { + b |= (bytes[2] as u64) << 16; + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 2 => { + b |= (bytes[1] as u64) << 8; + b |= bytes[0] as u64; + } + 1 => { + b |= bytes[0] as u64; + } + _ => {} + } + + b +} + +// LLVM siphash<2,4> 64-bit output +fn llvm_siphash_2_4_64(data: &[u8]) -> u64 { + let k0 = u64_from_le(&SIPHASH_KEY[0..8]); + let k1 = u64_from_le(&SIPHASH_KEY[8..16]); + + let mut v0: u64 = 0x736f6d6570736575 ^ k0; + let mut v1: u64 = 0x646f72616e646f6d ^ k1; + let mut v2: u64 = 0x6c7967656e657261 ^ k0; + let mut v3: u64 = 0x7465646279746573 ^ k1; + + let mut b: u64 = (data.len() as u64) << 56; + let mut i = 0; + + // compression + while i + 8 <= data.len() { + let m = u64_from_le(&data[i..i + 8]); + i += 8; + + v3 ^= m; + + for _ in 0..2 { + sipround(&mut v0, &mut v1, &mut v2, &mut v3); + } + + v0 ^= m; + } + + // tail + let tail = &data[i..]; + + b |= load_u64_partial(tail); + + v3 ^= b; + + for _ in 0..2 { + sipround(&mut v0, &mut v1, &mut v2, &mut v3); + } + + v0 ^= b; + + // finalization + v2 ^= 0xff; + + for _ in 0..4 { + sipround(&mut v0, &mut v1, &mut v2, &mut v3); + } + + v0 ^ v1 ^ v2 ^ v3 +} diff --git a/compiler/rustc_middle/src/ptrauth/mod.rs b/compiler/rustc_middle/src/ptrauth/mod.rs new file mode 100644 index 0000000000000..ec874c3ef9015 --- /dev/null +++ b/compiler/rustc_middle/src/ptrauth/mod.rs @@ -0,0 +1,7 @@ +pub mod discriminator; +pub mod llvm_siphash; + +pub use discriminator::{ + FnPtrDiscriminatorSource, FnPtrTypeDiscriminatorInput, clone_discriminated_ptrauth_schema_for, + compute_fn_ptr_type_discriminator_for, +}; From d85aa6b4f34378fdb9d50b59e4895442d414fa19 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:01:47 +0000 Subject: [PATCH 2/4] [PAC] Include discriminator in `FnAbi`, add `llvm.ptrauth.resign` This patch introduces the following: * Extends `FnAbi` (`callconv`) with a `ptrauth_type_discriminator` field. This field is only used when emitting pointer authentication call bundles. It is stored in `FnAbi` because the call site is not guaranteed to have access to an `Instance`, so the discriminator cannot always be computed on demand. * Adds support for `llvm.ptrauth.resign`. This intrinsic will be used when support for semantic transmute is added. * Performs a minor API redesign as groundwork for allowing call sites to modify schemas in place. --- compiler/rustc_codegen_gcc/src/builder.rs | 11 ++++++ compiler/rustc_codegen_gcc/src/common.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_gcc/src/int.rs | 1 + compiler/rustc_codegen_llvm/src/builder.rs | 34 +++++++++++++++++-- compiler/rustc_codegen_llvm/src/common.rs | 8 ++--- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../rustc_codegen_ssa/src/traits/builder.rs | 9 +++++ .../rustc_codegen_ssa/src/traits/consts.rs | 2 +- compiler/rustc_codegen_ssa/src/traits/misc.rs | 2 +- compiler/rustc_session/src/session.rs | 25 +++++++++++--- compiler/rustc_target/src/callconv/mod.rs | 15 ++++++-- compiler/rustc_ty_utils/src/abi.rs | 7 ++++ tests/ui/abi/c-zst.aarch64-darwin.stderr | 1 + tests/ui/abi/c-zst.powerpc-linux.stderr | 1 + tests/ui/abi/c-zst.s390x-linux.stderr | 1 + tests/ui/abi/c-zst.sparc64-linux.stderr | 1 + tests/ui/abi/c-zst.x86_64-linux.stderr | 1 + .../ui/abi/c-zst.x86_64-pc-windows-gnu.stderr | 1 + tests/ui/abi/debug.generic.stderr | 12 +++++++ tests/ui/abi/debug.loongarch64.stderr | 12 +++++++ tests/ui/abi/debug.riscv64.stderr | 12 +++++++ .../x86-64-sysv64-arg-ext.apple.stderr | 6 ++++ .../x86-64-sysv64-arg-ext.other.stderr | 6 ++++ tests/ui/abi/pass-indirectly-attr.stderr | 2 ++ tests/ui/abi/sysv64-zst.stderr | 1 + .../pass-by-value-abi.aarch64.stderr | 1 + .../c-variadic/pass-by-value-abi.win.stderr | 1 + .../pass-by-value-abi.x86_64.stderr | 3 ++ 29 files changed, 164 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index da17f05bb8a32..c28e1c1b86d64 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1843,6 +1843,17 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn fptosi_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { self.fptoint_sat(true, val, dest_ty) } + + fn ptrauth_resign( + &mut self, + _value: Self::Value, + _old_key: u32, + _old_discriminator: u64, + _new_key: u32, + _new_discriminator: u64, + ) -> Self::Value { + bug!("Resigning of pointers not implemented"); + } } impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index e73b8aab54d73..edbd0ef7ad1c0 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -247,7 +247,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>, - _schema: Option<&PointerAuthSchema>, + _schema: Option, ) -> RValue<'gcc> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 0e3fa72fbcfb3..4194f7de4609f 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -401,7 +401,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - _pointer_auth_schema: Option<&PointerAuthSchema>, + _pointer_auth_schema: Option, ) -> RValue<'gcc> { let func_name = self.tcx.symbol_name(instance).name; diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index dfae4eceebe44..8e391ce1dd0e8 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -375,6 +375,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { fixed_count: 3, conv: CanonAbi::C, can_unwind: false, + ptrauth_type_discriminator: 0, }; fn_abi.adjust_for_foreign_abi(self.cx, ExternAbi::C { unwind: false }); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index af6c24018028a..636519cff35ec 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1542,6 +1542,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, '_> { @@ -2059,8 +2083,14 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { // bundles. // Once this is resolved, we should analyze each call and skip direct calls. See the // discussion in the rust-lang issue: - let key: u32 = 0; - let discriminator: u64 = 0; + + let key: u32 = self.sess().pointer_authentication_fn_ptr_key().unwrap() as u32; + let discriminator = if self.sess().pointer_authentication_fn_ptr_type_discrimination() { + fn_abi?.ptrauth_type_discriminator + } else { + 0 + }; + Some(llvm::OperandBundleBox::new( "ptrauth", &[self.const_u32(key), self.const_u64(discriminator)], diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 205e2e9a14701..0098d25345e5d 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -30,11 +30,9 @@ pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( cx: &CodegenCx<'ll, '_>, instance: Instance<'tcx>, llfn: &'ll llvm::Value, - schema: &PointerAuthSchema, + schema: PointerAuthSchema, ) -> &'ll llvm::Value { - if cx.tcx.sess.pointer_authentication_functions().is_none() { - return llfn; - } + assert!(cx.tcx.sess.pointer_authentication_functions().is_some()); // Only free functions or methods let def_id = instance.def_id(); @@ -317,7 +315,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { cv: Scalar, layout: abi::Scalar, llty: &'ll Type, - schema: Option<&PointerAuthSchema>, + schema: Option, ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 7ea30a5b4db6d..ecd020902ad9d 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -913,7 +913,7 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_fn_addr( &self, instance: Instance<'tcx>, - pointer_auth_schema: Option<&PointerAuthSchema>, + pointer_auth_schema: Option, ) -> &'ll Value { // When pointer authentication metadata is provided, `get_fn_addr` will // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 0d27ff90991b3..1f455221507be 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -667,4 +667,13 @@ pub trait BuilderMethods<'a, 'tcx>: fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; fn apply_attrs_to_cleanup_callsite(&mut self, llret: Self::Value); + + fn ptrauth_resign( + &mut self, + value: Self::Value, + old_key: u32, + old_discriminator: u64, + new_key: u32, + new_discriminator: u64, + ) -> Self::Value; } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index b4eba38d39c19..ad5c4443f2dbc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -47,7 +47,7 @@ pub trait ConstCodegenMethods: BackendTypes { cv: Scalar, layout: abi::Scalar, llty: Self::Type, - schema: Option<&PointerAuthSchema>, + schema: Option, ) -> Self::Value; fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index add7128a2974b..6589cd219885b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -22,7 +22,7 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn get_fn_addr( &self, instance: Instance<'tcx>, - pointer_auth_schema: Option<&PointerAuthSchema>, + pointer_auth_schema: Option, ) -> Self::Value; fn eh_personality(&self) -> Self::Function; fn sess(&self) -> &Session; diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 733470a7e0471..74770a12c7b94 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -96,6 +96,7 @@ pub enum PointerAuthARM8_3Key { } /// Forms of extra discrimination. +#[derive(Clone, Debug, PartialEq)] pub enum PointerAuthDiscrimination { /// No additional discrimination. None, @@ -108,6 +109,7 @@ pub enum PointerAuthDiscrimination { } /// Types of address discrimination. +#[derive(Clone, Debug)] pub enum PointerAuthAddressDiscriminator { /// Enable/disable hardware address discrimination. HardwareAddress(bool), @@ -116,6 +118,7 @@ pub enum PointerAuthAddressDiscriminator { Synthetic(u64), } +#[derive(Clone, Debug)] pub struct PointerAuthSchema { pub is_address_discriminated: PointerAuthAddressDiscriminator, pub discrimination_kind: PointerAuthDiscrimination, @@ -1193,12 +1196,26 @@ impl Session { self.pointer_auth_config.is_some() } - pub fn pointer_authentication_functions(&self) -> Option<&PointerAuthSchema> { - self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()) + pub fn pointer_authentication_functions(&self) -> Option { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.clone()) } - pub fn pointer_authentication_init_fini(&self) -> Option<&PointerAuthSchema> { - self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()) + pub fn pointer_authentication_init_fini(&self) -> Option { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.clone()) + } + + pub fn pointer_authentication_fn_ptr_type_discrimination(&self) -> bool { + self.pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + .is_some_and(|schema| schema.discrimination_kind == PointerAuthDiscrimination::Type) + } + + pub fn pointer_authentication_fn_ptr_key(&self) -> Option { + self.pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + .map(|schema| schema.key) } } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 54f4ff77627b9..329ecfde0a11b 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -618,12 +618,22 @@ pub struct FnAbi<'a, Ty> { pub conv: CanonAbi, /// Indicates if an unwind may happen across a call to this function. pub can_unwind: bool, + /// Computed type discriminator for pointer authentication purpose. + pub ptrauth_type_discriminator: u64, } // Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl. impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let FnAbi { args, ret, c_variadic, fixed_count, conv, can_unwind } = self; + let FnAbi { + args, + ret, + c_variadic, + fixed_count, + conv, + can_unwind, + ptrauth_type_discriminator, + } = self; f.debug_struct("FnAbi") .field("args", args) .field("ret", ret) @@ -631,6 +641,7 @@ impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> { .field("fixed_count", fixed_count) .field("conv", conv) .field("can_unwind", can_unwind) + .field("ptrauth_type_discriminator", ptrauth_type_discriminator) .finish() } } @@ -930,6 +941,6 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(ArgAbi<'_, usize>, 56); - static_assert_size!(FnAbi<'_, usize>, 80); + static_assert_size!(FnAbi<'_, usize>, 88); // tidy-alphabetical-end } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index baf7d95741cac..1b063d55c75ff 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -6,6 +6,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, find_attr}; use rustc_middle::bug; use rustc_middle::middle::deduced_param_attrs::DeducedParamAttrs; +use rustc_middle::ptrauth::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, @@ -632,6 +633,12 @@ fn fn_abi_new_uncached<'tcx>( determined_fn_def_id, sig.abi(), ), + ptrauth_type_discriminator: if tcx.sess.pointer_authentication_fn_ptr_type_discrimination() + { + compute_fn_ptr_type_discriminator_for(tcx, sig).unwrap_or(0).into() + } else { + 0 + }, }; fn_abi_adjust_for_abi(cx, &mut fn_abi, sig.abi()); debug!("fn_abi_new_uncached = {:?}", fn_abi); diff --git a/tests/ui/abi/c-zst.aarch64-darwin.stderr b/tests/ui/abi/c-zst.aarch64-darwin.stderr index 6d2ac90c0c975..7981923585305 100644 --- a/tests/ui/abi/c-zst.aarch64-darwin.stderr +++ b/tests/ui/abi/c-zst.aarch64-darwin.stderr @@ -59,6 +59,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.powerpc-linux.stderr b/tests/ui/abi/c-zst.powerpc-linux.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.powerpc-linux.stderr +++ b/tests/ui/abi/c-zst.powerpc-linux.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.s390x-linux.stderr b/tests/ui/abi/c-zst.s390x-linux.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.s390x-linux.stderr +++ b/tests/ui/abi/c-zst.s390x-linux.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.sparc64-linux.stderr b/tests/ui/abi/c-zst.sparc64-linux.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.sparc64-linux.stderr +++ b/tests/ui/abi/c-zst.sparc64-linux.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.x86_64-linux.stderr b/tests/ui/abi/c-zst.x86_64-linux.stderr index 6d2ac90c0c975..7981923585305 100644 --- a/tests/ui/abi/c-zst.x86_64-linux.stderr +++ b/tests/ui/abi/c-zst.x86_64-linux.stderr @@ -59,6 +59,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr index edea2d5772280..03f3ffd295be0 100644 --- a/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr +++ b/tests/ui/abi/c-zst.x86_64-pc-windows-gnu.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(pass_zst) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/c-zst.rs:65:1 | diff --git a/tests/ui/abi/debug.generic.stderr b/tests/ui/abi/debug.generic.stderr index 125150f2c2e3f..40ef1a7450def 100644 --- a/tests/ui/abi/debug.generic.stderr +++ b/tests/ui/abi/debug.generic.stderr @@ -121,6 +121,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -217,6 +218,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -295,6 +297,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -379,6 +382,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -451,6 +455,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -530,6 +535,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -603,6 +609,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -680,6 +687,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -752,6 +760,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -830,6 +839,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -902,6 +912,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -1007,6 +1018,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.loongarch64.stderr b/tests/ui/abi/debug.loongarch64.stderr index 5f05174c12760..b50e92966d95a 100644 --- a/tests/ui/abi/debug.loongarch64.stderr +++ b/tests/ui/abi/debug.loongarch64.stderr @@ -121,6 +121,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -217,6 +218,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -295,6 +297,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -379,6 +382,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -451,6 +455,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -530,6 +535,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -603,6 +609,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -680,6 +687,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -752,6 +760,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -830,6 +839,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -902,6 +912,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -1007,6 +1018,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/debug.riscv64.stderr b/tests/ui/abi/debug.riscv64.stderr index 5f05174c12760..b50e92966d95a 100644 --- a/tests/ui/abi/debug.riscv64.stderr +++ b/tests/ui/abi/debug.riscv64.stderr @@ -121,6 +121,7 @@ error: fn_abi_of(test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:31:1 | @@ -217,6 +218,7 @@ error: fn_abi_of(TestFnPtr) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:37:1 | @@ -295,6 +297,7 @@ error: fn_abi_of(test_generic) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:40:1 | @@ -379,6 +382,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -451,6 +455,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:59:1 | @@ -530,6 +535,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -603,6 +609,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:62:1 | @@ -680,6 +687,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -752,6 +760,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:65:1 | @@ -830,6 +839,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } right ABI = FnAbi { args: [ @@ -902,6 +912,7 @@ error: ABIs are not compatible fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:69:1 | @@ -1007,6 +1018,7 @@ error: fn_abi_of(assoc_test) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: $SOME_BOOL, + ptrauth_type_discriminator: 0, } --> $DIR/debug.rs:52:5 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr index 02015d2a2e51f..f875a14411fd1 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.apple.stderr @@ -81,6 +81,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -170,6 +171,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -259,6 +261,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -348,6 +351,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -437,6 +441,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -526,6 +531,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr index 9bb2ab45d9841..7ea941662e9ca 100644 --- a/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr +++ b/tests/ui/abi/numbers-arithmetic/x86-64-sysv64-arg-ext.other.stderr @@ -81,6 +81,7 @@ error: fn_abi_of(i8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:13:1 | @@ -170,6 +171,7 @@ error: fn_abi_of(u8) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:19:1 | @@ -259,6 +261,7 @@ error: fn_abi_of(i16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:25:1 | @@ -348,6 +351,7 @@ error: fn_abi_of(u16) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:31:1 | @@ -437,6 +441,7 @@ error: fn_abi_of(i32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:37:1 | @@ -526,6 +531,7 @@ error: fn_abi_of(u32) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/x86-64-sysv64-arg-ext.rs:43:1 | diff --git a/tests/ui/abi/pass-indirectly-attr.stderr b/tests/ui/abi/pass-indirectly-attr.stderr index 320840c8149f5..e02eed82ad85e 100644 --- a/tests/ui/abi/pass-indirectly-attr.stderr +++ b/tests/ui/abi/pass-indirectly-attr.stderr @@ -83,6 +83,7 @@ error: fn_abi_of(extern_c) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-indirectly-attr.rs:20:1 | @@ -174,6 +175,7 @@ error: fn_abi_of(extern_rust) = FnAbi { fixed_count: 1, conv: Rust, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-indirectly-attr.rs:27:1 | diff --git a/tests/ui/abi/sysv64-zst.stderr b/tests/ui/abi/sysv64-zst.stderr index 82d3793c35328..7c375cb593a6c 100644 --- a/tests/ui/abi/sysv64-zst.stderr +++ b/tests/ui/abi/sysv64-zst.stderr @@ -61,6 +61,7 @@ error: fn_abi_of(pass_zst) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/sysv64-zst.rs:8:1 | diff --git a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr index 45edd7bc0e0ee..b470e4935f5f2 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.aarch64.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:27:1 | diff --git a/tests/ui/c-variadic/pass-by-value-abi.win.stderr b/tests/ui/c-variadic/pass-by-value-abi.win.stderr index b8e3f699b30e8..8b2cb0773ae13 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.win.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.win.stderr @@ -73,6 +73,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:27:1 | diff --git a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr index 1e203b93e66b3..e6daafcbb2fd6 100644 --- a/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr +++ b/tests/ui/c-variadic/pass-by-value-abi.x86_64.stderr @@ -70,6 +70,7 @@ error: fn_abi_of(take_va_list) = FnAbi { fixed_count: 1, conv: C, can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:27:1 | @@ -150,6 +151,7 @@ error: fn_abi_of(take_va_list_sysv64) = FnAbi { SysV64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:37:1 | @@ -230,6 +232,7 @@ error: fn_abi_of(take_va_list_win64) = FnAbi { Win64, ), can_unwind: false, + ptrauth_type_discriminator: 0, } --> $DIR/pass-by-value-abi.rs:44:1 | From 951b6709c88f26ff7cf1848a553709478058d971 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:16:40 +0000 Subject: [PATCH 3/4] [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 d1989609cefe2..20e7cb288ad5b 100644 --- a/compiler/rustc_session/src/diagnostics.rs +++ b/compiler/rustc_session/src/diagnostics.rs @@ -381,12 +381,6 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } -#[derive(Diagnostic)] -#[diag("function pointer type discrimination is not supported")] -pub(crate) struct PointerAuthenticationTypeDiscriminationNotSupportedForTarget<'a> { - pub(crate) target_triple: &'a TargetTuple, -} - #[derive(Diagnostic)] #[diag( "`-Z pointer-authentication` is not supported for target {$target_triple} and will be ignored" diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 74770a12c7b94..5045370cdb67c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -209,8 +209,7 @@ impl PointerAuthConfig { const GOT: u32 = 8; const GOTOS: u32 = 9; const TYPEINFO_VT_PTR_DISCR: u32 = 10; - // FIXME(jchlanda) We don't yet support function pointer type discrimination. - // const FPTR_TYPE_DISCR: u32 = 11; + const FPTR_TYPE_DISCR: u32 = 11; let pauth_abi_version: u32 = (u32::from(self.intrinsics) << INTRINSICS) | (u32::from(self.function_pointers.is_some()) << CALLS) @@ -228,7 +227,10 @@ impl PointerAuthConfig { })) << INIT_FINI_ADDR_DISC) | (u32::from(self.elf_got) << GOT) | (u32::from(self.indirect_gotos) << GOTOS) - | (u32::from(self.typeinfo_vt_ptr_discrimination) << TYPEINFO_VT_PTR_DISCR); + | (u32::from(self.typeinfo_vt_ptr_discrimination) << TYPEINFO_VT_PTR_DISCR) + | (u32::from(self.function_pointers.as_ref().is_some_and(|schema| { + matches!(schema.discrimination_kind, PointerAuthDiscrimination::Type) + })) << FPTR_TYPE_DISCR); pauth_abi_version } @@ -1446,19 +1448,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 539922687fe923fae7f82948b6343b4678eb5e63 Mon Sep 17 00:00:00 2001 From: Jakub Chlanda Date: Fri, 10 Jul 2026 12:59:12 +0000 Subject: [PATCH 4/4] [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_llvm/src/common.rs | 2 + compiler/rustc_codegen_llvm/src/consts.rs | 179 +++++++++++++++++++++- 2 files changed, 177 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 0098d25345e5d..af9846e281038 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -350,6 +350,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { alloc.inner(), IsStatic::No, IsInitOrFini::No, + None, ); let alloc = alloc.inner(); let value = match alloc.mutability { @@ -387,6 +388,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { alloc.inner(), IsStatic::No, IsInitOrFini::No, + None, ); self.static_addr_of_impl(init, alloc.inner().align, None) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 8f87acaf675a4..9e515296515ee 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -3,6 +3,7 @@ use std::ops::Range; use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::LangItem; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; @@ -13,8 +14,9 @@ use rustc_middle::mir::interpret::{ read_target_uint, }; use rustc_middle::mono::MonoItem; +use rustc_middle::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_middle::{bug, span_bug}; use rustc_span::Symbol; use rustc_target::spec::Arch; @@ -37,11 +39,152 @@ pub(crate) enum IsInitOrFini { Yes, No, } + +/// Maps offsets within a static allocation to the function pointer +/// discriminator that should be applied when authenticating the relocation +/// emitted at that offset. +/// +/// Offsets are relative to the beginning of the allocation. +pub(crate) struct FnPtrDiscriminatorAtOffset { + map: FxHashMap, +} + +/// Recursively walks a type layout and records the offsets of all extern "C" +/// function pointer fields together with their computed type discriminators. +/// +/// Traversal currently supports: +/// - direct function pointers +/// - transparent wrappers +/// - structs +/// - tuples +/// - arrays +/// +/// Offsets are accumulated relative to the containing object. +fn collect_fn_ptr_discriminators<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, +) -> FnPtrDiscriminatorAtOffset { + let mut map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner(tcx, typing_env, ty, Size::ZERO, &mut map); + + FnPtrDiscriminatorAtOffset { map } +} + +fn collect_fn_ptr_discriminators_inner<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + base_offset: Size, + map: &mut FxHashMap, +) { + // Direct function pointer. + if let Some(disc) = compute_fn_ptr_type_discriminator_for(tcx, ty) { + map.insert(base_offset, disc.into()); + + return; + } + + match ty.kind() { + ty::Adt(def, args) if def.repr().transparent() => { + let variant = def.non_enum_variant(); + + let Some((_, field)) = variant.fields.iter_enumerated().next() else { + return; + }; + + let field_ty = tcx.normalize_erasing_regions(typing_env, field.ty(tcx, args)); + + collect_fn_ptr_discriminators_inner(tcx, typing_env, field_ty, base_offset, map); + } + ty::Adt(def, args) if def.is_struct() => { + let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { + return; + }; + + let variant = def.non_enum_variant(); + + for (idx, field_def) in variant.fields.iter_enumerated() { + let field_ty = tcx.normalize_erasing_regions(typing_env, field_def.ty(tcx, args)); + + let field_offset = layout.fields.offset(idx.into()); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + field_ty, + base_offset + field_offset, + map, + ); + } + } + ty::Tuple(fields) => { + let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { + return; + }; + + for (idx, field_ty) in fields.iter().enumerate() { + let field_offset = layout.fields.offset(idx); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + field_ty, + base_offset + field_offset, + map, + ); + } + } + ty::Array(elem_ty, len) => { + let count = match len.try_to_target_usize(tcx) { + Some(v) => v, + None => return, + }; + + let Ok(elem_layout) = tcx.layout_of(typing_env.as_query_input(*elem_ty)) else { + return; + }; + + let stride = elem_layout.size; + + // Collect discriminator of one element, so we don't have to recompute it for all the + // elements in the array. + let mut elem_map = FxHashMap::default(); + + collect_fn_ptr_discriminators_inner( + tcx, + typing_env, + *elem_ty, + Size::ZERO, + &mut elem_map, + ); + + // SAFETY: We immediately collect into a Vec and sort by offset. + // The HashMap iteration order is irrelevant and must not affect determinism. + #[allow(rustc::potential_query_instability)] + let mut entries: Vec<(Size, u64)> = elem_map.into_iter().collect(); + entries.sort_unstable_by_key(|(offset, _)| *offset); + + // Replicate for every array slot. + for i in 0..count { + let elem_base = base_offset + stride * i; + + for (inner_offset, discr) in entries.iter().copied() { + map.insert(elem_base + inner_offset, discr); + } + } + } + _ => {} + } +} + pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, alloc: &Allocation, is_static: IsStatic, is_init_fini: IsInitOrFini, + fn_ptr_discriminators: Option<&FnPtrDiscriminatorAtOffset>, ) -> &'ll Value { // We expect that callers of const_alloc_to_llvm will instead directly codegen a pointer or // integer for any &ZST where the ZST is a constant (i.e. not a static). We should never be @@ -121,7 +264,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( as u64; let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); - let schema = if cx.sess().pointer_authentication() { + let mut schema = if cx.sess().pointer_authentication() { match is_init_fini { IsInitOrFini::Yes => cx.sess().pointer_authentication_init_fini(), IsInitOrFini::No => cx.sess().pointer_authentication_functions(), @@ -129,6 +272,16 @@ pub(crate) fn const_alloc_to_llvm<'ll>( } else { None }; + let discr = fn_ptr_discriminators + .as_ref() + .and_then(|m| m.map.get(&Size::from_bytes(offset as u64))); + + // Init/fini entries must not participate in function pointer type discrimination. + if let (Some(schema), Some(discr)) = (schema.as_mut(), discr) + && is_init_fini == IsInitOrFini::No + { + schema.constant_discriminator = *discr as u16; + } llvals.push(cx.scalar_to_backend_with_pac( InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx), Scalar::Initialized { @@ -160,6 +313,15 @@ fn codegen_static_initializer<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { + let fn_ptr_discriminators = if cx.sess().pointer_authentication_fn_ptr_type_discrimination() { + let instance = Instance::mono(cx.tcx, def_id); + let ty = instance.ty(cx.tcx, cx.typing_env()); + + Some(collect_fn_ptr_discriminators(cx.tcx, cx.typing_env(), ty)) + } else { + None + }; + let alloc = cx.tcx.eval_static_initializer(def_id)?; let attrs = cx.tcx.codegen_fn_attrs(def_id); // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion @@ -175,7 +337,16 @@ fn codegen_static_initializer<'ll, 'tcx>( } }) .unwrap_or(IsInitOrFini::No); - Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc)) + Ok(( + const_alloc_to_llvm( + cx, + alloc.inner(), + IsStatic::Yes, + is_in_init_fini, + fn_ptr_discriminators.as_ref(), + ), + alloc, + )) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -837,7 +1008,7 @@ impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { fn static_addr_of(&self, alloc: ConstAllocation<'_>, kind: Option<&str>) -> &'ll Value { // FIXME: should we cache `const_alloc_to_llvm` to avoid repeating this for the // same `ConstAllocation`? - let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); + let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No, None); let gv = self.static_addr_of_impl(cv, alloc.inner().align, kind); // static_addr_of_impl returns the bare global variable, which might not be in the default