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