diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index dd0064d34bc4a..25029436e923f 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -7,6 +7,7 @@ use rustc_codegen_ssa::traits::{ 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}; @@ -229,7 +230,13 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { None } - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> { + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + ty: Type<'gcc>, + _schema: Option<&PointerAuthSchema>, + ) -> RValue<'gcc> { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { @@ -278,7 +285,7 @@ impl<'gcc, 'tcx> ConstCodegenMethods for CodegenCx<'gcc, 'tcx> { } value } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, None), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index ed313859aeafa..0957727bd7865 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -16,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}; @@ -398,7 +398,11 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> { + fn get_fn_addr( + &self, + instance: Instance<'tcx>, + _pointer_auth_schema: Option<&PointerAuthSchema>, + ) -> 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_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 6295d05f5d508..9aedf427127bf 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -608,6 +608,13 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } } + 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)); + } + } + to_add.extend(target_features_attr(cx, tcx, function_features)); attributes::apply_to_llfn(llfn, Function, &to_add); diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index a7dec13422f46..1f2df013d95c5 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -123,7 +123,19 @@ pub(crate) fn compile_codegen_unit( if let Some(entry) = maybe_create_entry_wrapper::>(&cx, cx.codegen_unit) { - let attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + let mut attrs = attributes::sanitize_attrs(&cx, tcx, SanitizerFnAttrs::default()); + // When pointer authentication is enabled, ensure that the ptrauth-* attributes are + // also attached to the entry wrapper. + // + // 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().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)); + } + } attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } @@ -140,6 +152,24 @@ pub(crate) fn compile_codegen_unit( cx.add_objc_module_flags(); } + 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(); + } + if cx.sess().pointer_authentication_functions() { + cx.add_ptrauth_sign_personality_flag(); + } + } + // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.compiler.used` variable, created next. if cx.sess().instrument_coverage() { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 134bc5006dd00..7fd5c1a0eaeda 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -7,7 +7,7 @@ pub(crate) mod autodiff; pub(crate) mod gpu_offload; use libc::{c_char, c_uint}; -use rustc_abi::{self as abi, Align, Size, WrappingRange}; +use rustc_abi::{self as abi, Align, CanonAbi, Size, WrappingRange}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::{IntPredicate, RealPredicate, SynchronizationScope, TypeKind}; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; @@ -429,6 +429,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let invoke = unsafe { llvm::LLVMBuildInvokeWithOperandBundles( self.llbuilder, @@ -1406,6 +1411,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let call = unsafe { llvm::LLVMBuildCallWithOperandBundles( self.llbuilder, @@ -1836,6 +1846,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { bundles.push(kcfi_bundle); } + let pauth = self.ptrauth_operand_bundle(llfn, fn_abi); + if let Some(p) = pauth.as_ref().map(|b| b.as_ref()) { + bundles.push(p); + } + let callbr = unsafe { llvm::LLVMBuildCallBr( self.llbuilder, @@ -1955,6 +1970,40 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { kcfi_bundle } + // Emits pauth operand bundle. + fn ptrauth_operand_bundle( + &mut self, + llfn: &'ll Value, + fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + ) -> Option> { + if !self.sess().pointer_authentication_functions() { + return None; + } + // Pointer authentication support is currently limited to extern "C" calls; filter out other + // ABIs. + if fn_abi?.conv != CanonAbi::C { + return None; + } + // Filter out LLVM intrinsics. + if llvm::get_value_name(llfn).starts_with(b"llvm.") { + return None; + } + + // FIXME(jchlanda) Operand bundles should only be attached to indirect function calls. + // However, function pointer signing is currently performed in `get_fn_addr`, which causes + // the logic to be applied too broadly, including to function values (not just pointers). + // As a result, direct calls using signed function values must also receive operand + // 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; + Some(llvm::OperandBundleBox::new( + "ptrauth", + &[self.const_u32(key), self.const_u64(discriminator)], + )) + } + /// Emits a call to `llvm.instrprof.increment`. Used by coverage instrumentation. #[instrument(level = "debug", skip(self))] pub(crate) fn instrprof_increment( diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 5c5e9ed7e082c..030d62b25e92e 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -4,23 +4,67 @@ use std::borrow::Borrow; use libc::{c_char, c_uint}; use rustc_abi::Primitive::Pointer; -use rustc_abi::{self as abi, HasDataLayout as _}; +use rustc_abi::{self as abi, ExternAbi, HasDataLayout as _}; use rustc_ast::Mutability; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::*; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; use rustc_hashes::Hash128; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::mir::interpret::{GlobalAlloc, PointerArithmetic, Scalar}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{Instance, TyCtxt}; use rustc_session::cstore::DllImport; +use rustc_session::{PointerAuthAddressDiscriminator, PointerAuthSchema}; use tracing::debug; -use crate::consts::const_alloc_to_llvm; +use crate::consts::{IsInitOrFini, IsStatic, const_alloc_to_llvm}; pub(crate) use crate::context::CodegenCx; use crate::context::{GenericCx, SCx}; -use crate::llvm::{self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value}; +use crate::llvm::{ + self, BasicBlock, ConstantInt, FALSE, TRUE, ToLlvmBool, Type, Value, const_ptr_auth, +}; + +pub(crate) fn maybe_sign_fn_ptr<'ll, 'tcx>( + cx: &CodegenCx<'ll, '_>, + instance: Instance<'tcx>, + llfn: &'ll llvm::Value, + schema: &PointerAuthSchema, +) -> &'ll llvm::Value { + if !cx.tcx.sess.pointer_authentication_functions() { + return llfn; + } + + // Only free functions or methods + let def_id = instance.def_id(); + if !matches!(cx.tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { + return llfn; + } + // Only C ABI + let abi = cx.tcx.fn_sig(def_id).skip_binder().abi(); + if !matches!(abi, ExternAbi::C { .. } | ExternAbi::System { .. }) { + return llfn; + } + // Ignore LLVM intrinsics + if llvm::get_value_name(llfn).starts_with(b"llvm.") { + return llfn; + } + if Some(def_id) == cx.tcx.lang_items().eh_personality() { + return llfn; + } + + 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, schema.key as u32, schema.constant_discriminator as u64, addr_diversity) +} /* * A note on nomenclature of linking: "extern", "foreign", and "upcall". @@ -268,7 +312,13 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }) } - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: &'ll Type) -> &'ll Value { + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + llty: &'ll Type, + schema: Option<&PointerAuthSchema>, + ) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.size(self).bits() }; match cv { Scalar::Int(int) => { @@ -297,8 +347,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { self.const_bitcast(llval, llty) }; } else { - let init = - const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ); let alloc = alloc.inner(); let value = match alloc.mutability { Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), @@ -319,7 +373,7 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { value } } - GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance), + GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance, schema), GlobalAlloc::VTable(ty, dyn_ty) => { let alloc = self .tcx @@ -330,7 +384,12 @@ impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> { }), ))) .unwrap_memory(); - let init = const_alloc_to_llvm(self, alloc.inner(), /*static*/ false); + let init = const_alloc_to_llvm( + self, + alloc.inner(), + IsStatic::No, + IsInitOrFini::No, + ); self.static_addr_of_impl(init, alloc.inner().align, None) } GlobalAlloc::Static(def_id) => { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index ab3e4b8d9effa..aa2d976a9cab2 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -1,6 +1,6 @@ use std::ops::Range; -use rustc_abi::{Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; +use rustc_abi::{Align, ExternAbi, HasDataLayout, Primitive, Scalar, Size, WrappingRange}; use rustc_codegen_ssa::common; use rustc_codegen_ssa::traits::*; use rustc_hir::LangItem; @@ -22,14 +22,26 @@ use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; use crate::errors::SymbolAlreadyDefined; -use crate::llvm::{self, Type, Value}; +use crate::llvm::{self, Type, Value, const_ptr_auth}; use crate::type_of::LayoutLlvmExt; use crate::{base, debuginfo}; +/// Indicates whether a value originates from a `static`. +pub(crate) enum IsStatic { + Yes, + No, +} +/// Indicates whether a symbol is part of `.init_array` or `.fini_array`. +#[derive(PartialEq)] +pub(crate) enum IsInitOrFini { + Yes, + No, +} pub(crate) fn const_alloc_to_llvm<'ll>( cx: &CodegenCx<'ll, '_>, alloc: &Allocation, - is_static: bool, + is_static: IsStatic, + is_init_fini: IsInitOrFini, ) -> &'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 @@ -38,7 +50,7 @@ pub(crate) fn const_alloc_to_llvm<'ll>( // // Statics have a guaranteed meaningful address so it's less clear that we want to do // something like this; it's also harder. - if !is_static { + if matches!(is_static, IsStatic::No) { assert!(alloc.len() != 0); } let mut llvals = Vec::with_capacity(alloc.provenance().ptrs().len() + 1); @@ -109,14 +121,40 @@ pub(crate) fn const_alloc_to_llvm<'ll>( as u64; let address_space = cx.tcx.global_alloc(prov.alloc_id()).address_space(cx); - - llvals.push(cx.scalar_to_backend( + let schema = if cx.sess().pointer_authentication() { + match is_init_fini { + IsInitOrFini::Yes => { + if cx.sess().pointer_authentication_init_fini() { + cx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.init_fini.as_ref()) + } else { + None + } + } + IsInitOrFini::No => { + if cx.sess().pointer_authentication_functions() { + cx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + } else { + None + } + } + } + } else { + None + }; + llvals.push(cx.scalar_to_backend_with_pac( InterpScalar::from_pointer(Pointer::new(prov, Size::from_bytes(ptr_offset)), &cx.tcx), Scalar::Initialized { value: Primitive::Pointer(address_space), valid_range: WrappingRange::full(pointer_size), }, cx.type_ptr_ext(address_space), + schema, )); next_offset = offset + pointer_size_bytes; } @@ -141,7 +179,21 @@ fn codegen_static_initializer<'ll, 'tcx>( def_id: DefId, ) -> Result<(&'ll Value, ConstAllocation<'tcx>), ErrorHandled> { let alloc = cx.tcx.eval_static_initializer(def_id)?; - Ok((const_alloc_to_llvm(cx, alloc.inner(), /*static*/ true), alloc)) + let attrs = cx.tcx.codegen_fn_attrs(def_id); + // FIXME(jchlanda) Decide if this could be better served by `ctor` crate. See the discussion + // here: + let is_in_init_fini: IsInitOrFini = attrs + .link_section + .map(|link_section| { + let s = link_section.as_str(); + if s.starts_with(".init_array") || s.starts_with(".fini_array") { + IsInitOrFini::Yes + } else { + IsInitOrFini::No + } + }) + .unwrap_or(IsInitOrFini::No); + Ok((const_alloc_to_llvm(cx, alloc.inner(), IsStatic::Yes, is_in_init_fini), alloc)) } fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) { @@ -164,6 +216,7 @@ fn check_and_apply_linkage<'ll, 'tcx>( if let Some(linkage) = attrs.import_linkage { debug!("get_static: sym={} linkage={:?}", sym, linkage); + let mut should_sign = false; // Declare a symbol `foo`. If `foo` is an extern_weak symbol, we declare // an extern_weak function, otherwise a global with the desired linkage. let g1 = if matches!(attrs.import_linkage, Some(Linkage::ExternalWeak)) { @@ -176,8 +229,13 @@ fn check_and_apply_linkage<'ll, 'tcx>( && let ty::FnPtr(sig, header) = args.type_at(0).kind() { 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().pointer_authentication() + && matches!(fn_sig.abi(), ExternAbi::C { .. } | ExternAbi::System { .. }) + { + should_sign = true; + } cx.declare_fn(sym, &fn_abi, None) } else { cx.declare_global(sym, cx.type_i8()) @@ -207,7 +265,24 @@ fn check_and_apply_linkage<'ll, 'tcx>( }); llvm::set_linkage(g2, llvm::Linkage::InternalLinkage); llvm::set_unnamed_address(g2, llvm::UnnamedAddr::Global); - llvm::set_initializer(g2, g1); + + // Sign the function pointer that is used to initialize the global + let initializer = if should_sign { + let key: u32 = 0; + let discriminator: u64 = 0; + + const_ptr_auth( + cx.const_bitcast(g1, llty), + key, + discriminator, + None, /* address_diversity */ + ) + } else { + g1 + }; + + llvm::set_initializer(g2, initializer); + g2 } else if cx.tcx.sess.target.arch == Arch::X86 && common::is_mingw_gnu_toolchain(&cx.tcx.sess.target) @@ -776,7 +851,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(), /*static*/ false); + let cv = const_alloc_to_llvm(self, alloc.inner(), IsStatic::No, IsInitOrFini::No); 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 6e0ff1c75149b..f21f4167f8b84 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -21,10 +21,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_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{ @@ -722,6 +722,47 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } + pub(crate) fn add_ptrauth_elf_got_flag(&self) { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "ptrauth-elf-got", + 1, + ); + } + + pub(crate) fn add_ptrauth_sign_personality_flag(&self) { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "ptrauth-sign-personality", + 1, + ); + } + + 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 @@ -866,8 +907,28 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { get_fn(self, instance) } - fn get_fn_addr(&self, instance: Instance<'tcx>) -> &'ll Value { - get_fn(self, instance) + fn get_fn_addr( + &self, + instance: Instance<'tcx>, + pointer_auth_schema: Option<&PointerAuthSchema>, + ) -> &'ll Value { + // When pointer authentication metadata is provided, `get_fn_addr` will + // attempt to sign the pointer using LLVM's `ConstPtrAuth` constant + // expression. + // + // FIXME(jchlanda) Currently, all function addresses requested from + // within LLVM codegen are signed. This behavior is too broad, resulting + // in the logic being applied to function values, not just pointers + // (addresses). + // + // See the discussion in the rust-lang issue: + // , and comment in + // builder's `ptrauth_operand_bundle`. + let llfn = get_fn(self, instance); + match pointer_auth_schema { + Some(schema) => common::maybe_sign_fn_ptr(self, instance, llfn, schema), + None => llfn, + } } fn eh_personality(&self) -> &'ll Value { @@ -909,13 +970,19 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { - Some(def_id) if name.is_none() => self.get_fn_addr(ty::Instance::expect_resolve( - tcx, - self.typing_env(), - def_id, - ty::List::empty(), - DUMMY_SP, - )), + Some(def_id) if name.is_none() => self.get_fn_addr( + ty::Instance::expect_resolve( + tcx, + self.typing_env(), + def_id, + ty::List::empty(), + DUMMY_SP, + ), + tcx.sess + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), _ => { let name = name.unwrap_or("rust_eh_personality"); if let Some(llfn) = self.get_declared_value(name) { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 1c7b415fd04c7..814387822abe8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -43,7 +43,7 @@ use crate::errors::{ AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, IntrinsicWrongArch, OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic, }; -use crate::llvm::{self, Type, Value}; +use crate::llvm::{self, Attribute, AttributePlace, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -1788,6 +1788,16 @@ 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().pointer_authentication() { + let cfg = cx.sess().pointer_auth_config.as_ref().unwrap(); + let attrs: Vec<&Attribute> = + 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); + } + cx.rust_try_fn.set(Some(rust_try)); rust_try } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 1fde5866f5dca..35c5871f8e559 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2639,4 +2639,12 @@ unsafe extern "C" { Aliasee: &Value, Name: *const c_char, ) -> &'ll Value; + + pub(crate) fn LLVMRustConstPtrAuth( + ptr: *const Value, + key: u32, + disc: u64, + addr_diversity: *const Value, + deactivation_symbol: *const Value, + ) -> *const Value; } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index 2ec19b1795b5a..e9bc6ae0e80ef 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -475,3 +475,19 @@ pub(crate) fn add_alias<'ll>( ) -> &'ll Value { unsafe { LLVMAddAlias2(module, ty, address_space.0, aliasee, name.as_ptr()) } } + +/// Safe wrapper for `LLVMRustConstPtrAuth`. +pub(crate) fn const_ptr_auth<'ll>( + ptr: &'ll Value, + key: u32, + disc: u64, + addr_diversity: Option<&'ll Value>, +) -> &'ll Value { + unsafe { + let addr_div_ptr = addr_diversity.map_or(std::ptr::null(), |v| v as *const Value); + let deactivation_symbol = std::ptr::null(); + let result = + LLVMRustConstPtrAuth(ptr as *const Value, key, disc, addr_div_ptr, deactivation_symbol); + &*result + } +} diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 78bc07869895a..b1972ca27fa44 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -494,7 +494,10 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return None; } - let main_llfn = cx.get_fn_addr(instance); + let main_llfn = cx.get_fn_addr( + instance, + cx.sess().pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()), + ); let entry_fn = create_entry_fn::(cx, main_llfn, main_def_id, entry_type); return Some(entry_fn); @@ -555,7 +558,13 @@ 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); + let start_fn = cx.get_fn_addr( + start_instance, + cx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ); 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 eca013c83cb4a..cf3a9bb57313c 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -117,7 +117,14 @@ 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), instance) + ( + bx.fn_abi_of_instance(instance, ty::List::empty()), + bx.get_fn_addr( + instance, + tcx.sess.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()), + ), + 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 045595ff42785..159ecdfc5ed00 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -657,7 +657,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => ( false, - bx.get_fn_addr(drop_fn), + bx.get_fn_addr( + drop_fn, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), bx.fn_abi_of_instance(drop_fn, ty::List::empty()), drop_fn, ), @@ -1068,7 +1074,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { ) .unwrap(); - (None, Some(bx.get_fn_addr(instance))) + ( + None, + Some( + bx.get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), + ), + ) } _ => (Some(instance), None), } @@ -1374,7 +1391,13 @@ 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(instance), None) => bx.get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), (_, 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 90ac8c89ba9ad..cc03add062971 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -423,7 +423,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ) .unwrap(); - OperandValue::Immediate(bx.get_fn_addr(instance)) + OperandValue::Immediate( + bx.get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), + ) } _ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty), } @@ -437,7 +445,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args, ty::ClosureKind::FnOnce, ); - OperandValue::Immediate(bx.cx().get_fn_addr(instance)) + OperandValue::Immediate( + bx.cx().get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ), + ) } _ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty), } @@ -652,7 +668,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { def: ty::InstanceKind::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), }; - let fn_ptr = bx.get_fn_addr(instance); + let fn_ptr = bx.get_fn_addr( + instance, + bx.sess() + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()), + ); 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/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 22784a8868ab5..b4eba38d39c19 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -1,5 +1,6 @@ use rustc_abi as abi; use rustc_middle::mir::interpret::Scalar; +use rustc_session::PointerAuthSchema; use super::BackendTypes; @@ -38,7 +39,16 @@ pub trait ConstCodegenMethods: BackendTypes { fn const_to_opt_uint(&self, v: Self::Value) -> Option; fn const_to_opt_u128(&self, v: Self::Value, sign_ext: bool) -> Option; - fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value; + fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value { + self.scalar_to_backend_with_pac(cv, layout, llty, None) + } + fn scalar_to_backend_with_pac( + &self, + cv: Scalar, + layout: abi::Scalar, + llty: Self::Type, + schema: Option<&PointerAuthSchema>, + ) -> 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 92ddc1f347994..add7128a2974b 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -2,7 +2,7 @@ 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; @@ -19,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>) -> Self::Value; + fn get_fn_addr( + &self, + instance: Instance<'tcx>, + pointer_auth_schema: Option<&PointerAuthSchema>, + ) -> 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_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index ce38ba8338338..b1d0e12d75f15 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1841,6 +1841,35 @@ extern "C" bool LLVMRustIsTargetIntrinsic(unsigned ID) { return Intrinsic::isTargetIntrinsic(ID); } +extern "C" LLVMValueRef LLVMRustConstPtrAuth(LLVMValueRef Ptr, uint32_t Key, + uint64_t Disc, + LLVMValueRef AddrDiversity, + LLVMValueRef DeactivationSymbol) { + auto *C = cast(unwrap(Ptr)); + assert(C->getType()->isPointerTy() && "Expected pointer type"); + assert(!isa(C) && "Unexpected undef in const_ptr_auth"); + assert(!isa(C) && "Unexpected null in const_ptr_auth"); + + LLVMContext &Ctx = C->getContext(); + auto *KeyC = ConstantInt::get(Type::getInt32Ty(Ctx), Key); + auto *DiscC = ConstantInt::get(Type::getInt64Ty(Ctx), Disc); + auto *PTy = cast(C->getType()); + Constant *AddrDiv = + AddrDiversity ? dyn_cast(unwrap(AddrDiversity)) + : ConstantPointerNull::get(cast(C->getType())); + assert(AddrDiv && "Failed to get Address Diversity"); +#if LLVM_VERSION_GE(22, 0) + Constant *DeactivationSym = + DeactivationSymbol ? dyn_cast(unwrap(DeactivationSymbol)) + : ConstantPointerNull::get(PTy); + assert(DeactivationSym && "Failed to get Deactivation Symbol"); + + return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv, DeactivationSym)); +#else + return wrap(ConstantPtrAuth::get(C, KeyC, DiscC, AddrDiv)); +#endif +} + // Statically assert that the fixed metadata kind IDs declared in // `metadata_kind.rs` match the ones actually used by LLVM. #define FIXED_MD_KIND(VARIANT, VALUE) \ diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 5c33fab5011d1..659406f8c84e9 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -704,3 +704,18 @@ pub(crate) struct MitigationLessStrictInDependency { pub mitigation_level: String, pub extern_crate: Symbol, } + +#[derive(Diagnostic)] +pub(crate) enum StaticLinkingNotSupported<'a> { + #[diag( + "static linking of `{$lib_name}` is not supported on `{$target}`; using dynamic linking instead" + )] + #[help("remove `kind = \"static\"` and ensure a shared library is available")] + UserRequested { lib_name: Symbol, target: &'a str }, + + #[diag( + "library `{$lib_name}` is linked statically by a dependency, but `{$target}` requires dynamic linking; using dynamic linking instead" + )] + #[help("ensure a shared library is available")] + FromDependency { lib_name: Symbol, target: &'a str }, +} diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index bfe7e01dc85e7..b8a45a596b1fb 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -196,6 +196,31 @@ pub(crate) fn collect(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> Vec } } collector.process_command_line(); + for lib in &mut collector.libs { + // FIXME(jchlanda) Pauthtest does not support static linking. It must be dynamically linked, + // with a dynamic linker acting as the ELF interpreter that can resolve pauth relocations + // and enforce pointer authentication constraints. + if tcx.sess.target.cfg_abi == CfgAbi::Pauthtest { + if let NativeLibKind::Static { .. } = lib.kind { + if !tcx.sess.opts.unstable_opts.ui_testing { + let diag = if lib.foreign_module.is_none() { + errors::StaticLinkingNotSupported::UserRequested { + lib_name: lib.name, + target: tcx.sess.target.llvm_target.as_ref(), + } + } else { + errors::StaticLinkingNotSupported::FromDependency { + lib_name: lib.name, + target: tcx.sess.target.llvm_target.as_ref(), + } + }; + tcx.dcx().emit_warn(diag); + } + + lib.kind = NativeLibKind::Dylib { as_needed: None }; + } + } + } collector.libs } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e82f67eac5e9f..97001d8a2b156 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1565,6 +1565,46 @@ pub struct BranchProtection { pub gcs: bool, } +#[derive(Clone, Copy, Hash, Debug, PartialEq)] +pub enum PointerAuthOption { + // See and Clang's command line reference: + // + // for the origin and meaning of the enum values. + Aarch64JumpTableHardening, + AuthTraps, + Calls, + ElfGot, + FunctionPointerTypeDiscrimination, + IndirectGotos, + InitFini, + InitFiniAddressDiscrimination, + Intrinsics, + ReturnAddresses, + TypeInfoVTPtrDisc, + VTPtrAddrDisc, + VTPtrTypeDisc, +} +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); @@ -3049,8 +3089,9 @@ pub(crate) mod dep_tracking { CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FmtDebug, FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli, MirStripDebugInfo, NextSolverConfig, Offload, OptLevel, OutFileName, OutputType, - OutputTypes, PatchableFunctionEntry, Polonius, ResolveDocLinks, SourceFileHashAlgorithm, - SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel, + OutputTypes, PatchableFunctionEntry, PointerAuthOption, Polonius, ResolveDocLinks, + SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, + WasiExecModel, }; use crate::lint; use crate::utils::NativeLib; @@ -3154,6 +3195,7 @@ pub(crate) mod dep_tracking { InliningThreshold, FunctionReturn, Align, + PointerAuthOption, ); impl DepTrackingHash for (T1, T2) diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index bc4c5e98282a5..515aa3fb8319a 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -307,6 +307,12 @@ pub(crate) struct CannotMixAndMatchSanitizers { )] pub(crate) struct CannotEnableCrtStaticLinux; +#[derive(Diagnostic)] +#[diag( + "pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static`" +)] +pub(crate) struct CannotEnableCrtStaticPointerAuth; + #[derive(Diagnostic)] #[diag("`-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`")] pub(crate) struct SanitizerCfiRequiresLto; @@ -371,6 +377,20 @@ pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } +#[derive(Diagnostic)] +#[diag("function pointer type discrimination is not supported")] +pub(crate) struct PointerAuthenticationTypeDiscriminationNotSupportedForTarget<'a> { + pub(crate) target_triple: &'a TargetTuple, +} + +#[derive(Diagnostic)] +#[diag( + "`-Z pointer-authentication` is not supported for target {$target_triple} and will be ignored" +)] +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 de606458d048e..fd5f6415ce518 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -752,6 +752,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`"; @@ -1012,6 +1013,28 @@ pub mod parse { } } + pub(crate) fn parse_pointer_authentication_list_with_polarity( + slot: &mut Vec<(PointerAuthOption, bool)>, + v: Option<&str>, + ) -> bool { + match v { + Some(s) => { + for item in s.split(',') { + let Some(name) = item.strip_prefix(&['+', '-'][..]) else { + return false; + }; + let Some(opt) = PointerAuthOption::parse(name) else { + return false; // failed to parse, return. + }; + let enabled = &item[..1] == "+"; + slot.push((opt, enabled)); + } + true + } + None => false, + } + } + pub(crate) fn parse_fmt_debug(opt: &mut FmtDebug, v: Option<&str>) -> bool { *opt = match v { Some("full") => FmtDebug::Full, @@ -2533,6 +2556,22 @@ 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], + "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], diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c83bb62324e76..3bfeb645ccde1 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -29,9 +29,9 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::{RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ - Arch, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, - SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, - TargetTuple, TlsModel, apple, + Arch, CfgAbi, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, + SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, + Target, TargetTuple, TlsModel, apple, }; use crate::code_stats::CodeStats; @@ -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, OptLevel, OutFileName, OutputType, - SwitchWithOptPath, + PointerAuthOption, SwitchWithOptPath, }; use crate::filesearch::FileSearch; use crate::lint::LintId; @@ -85,6 +85,245 @@ 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. +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. +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), +} + +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; + // FIXME(jchlanda) We don't yet support function pointer type discrimination. + // 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); + + 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 { @@ -181,6 +420,8 @@ pub struct Session { /// /// The value is the `DepNodeIndex` of the node encodes the used feature. pub used_features: Lock>, + + pub pointer_auth_config: Option, } #[derive(Clone, Copy)] @@ -943,6 +1184,18 @@ 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) -> bool { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.function_pointers.as_ref()).is_some() + } + + pub fn pointer_authentication_init_fini(&self) -> bool { + self.pointer_auth_config.as_ref().and_then(|cfg| cfg.init_fini.as_ref()).is_some() + } } // JUSTIFICATION: part of session construction @@ -1098,6 +1351,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, @@ -1131,6 +1387,7 @@ pub fn build_session( thin_lto_supported: true, // filled by `run_compiler` mir_opt_bisect_eval_count: AtomicUsize::new(0), used_features: Lock::default(), + pointer_auth_config, }; validate_commandline_args_with_session_available(&sess); @@ -1159,6 +1416,25 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::LinkerPluginToWindowsNotSupported); } + if sess + .pointer_auth_config + .as_ref() + .and_then(|cfg| cfg.function_pointers.as_ref()) + .is_some_and(|schema| matches!(schema.discrimination_kind, PointerAuthDiscrimination::Type)) + { + sess.dcx().emit_err(errors::PointerAuthenticationTypeDiscriminationNotSupportedForTarget { + target_triple: &sess.opts.target_triple, + }); + } + + 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 { @@ -1218,6 +1494,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux); } + // FIXME(jchlanda) Pauthtest does not support static linking. It must be dynamically linked, + // with a dynamic linker acting as the ELF interpreter that can resolve pauth relocations and + // enforce pointer authentication constraints. + if sess.crt_static(None) && sess.target.cfg_abi == CfgAbi::Pauthtest { + sess.dcx().emit_err(errors::CannotEnableCrtStaticPointerAuth); + } + // LLVM CFI requires LTO. if sess.is_sanitizer_cfi_enabled() && !(sess.lto() == config::Lto::Fat || sess.opts.cg.linker_plugin_lto.enabled()) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index ea6abe5491262..b3531fde8e2e6 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1490,6 +1490,7 @@ supported_targets! { ("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf), ("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu), ("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl), + ("aarch64-unknown-linux-pauthtest", aarch64_unknown_linux_pauthtest), ("aarch64_be-unknown-linux-musl", aarch64_be_unknown_linux_musl), ("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl), ("i686-unknown-linux-musl", i686_unknown_linux_musl), @@ -2074,6 +2075,7 @@ crate::target_spec_enum! { Ilp32e = "ilp32e", Llvm = "llvm", MacAbi = "macabi", + Pauthtest = "pauthtest", Sim = "sim", SoftFloat = "softfloat", Spe = "spe", @@ -2114,6 +2116,8 @@ crate::target_spec_enum! { // PowerPC ElfV1 = "elfv1", ElfV2 = "elfv2", + // Pointer authentication: Pauthtest + Pauthtest = "pauthtest", Unspecified = "", } @@ -3394,9 +3398,10 @@ impl Target { ) } Arch::AArch64 => { - check!( - self.llvm_abiname == LlvmAbi::Unspecified, - "`llvm_abiname` is unused on aarch64" + check_matches!( + self.llvm_abiname, + LlvmAbi::Unspecified | LlvmAbi::Pauthtest, + "invalid llvm ABI for aarch64" ); check!(self.llvm_floatabi.is_none(), "`llvm_floatabi` is unused on aarch64"); // FIXME: Ensure that target_abi = "ilp32" correlates with actually using that ABI. @@ -3409,6 +3414,7 @@ impl Target { CfgAbi::Ilp32 | CfgAbi::Llvm | CfgAbi::MacAbi + | CfgAbi::Pauthtest | CfgAbi::Sim | CfgAbi::Uwp | CfgAbi::Unspecified diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs new file mode 100644 index 0000000000000..739694ea1509d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_pauthtest.rs @@ -0,0 +1,38 @@ +use crate::spec::{ + Arch, CfgAbi, Env, FramePointer, LinkSelfContainedDefault, LlvmAbi, StackProbeType, Target, + TargetMetadata, TargetOptions, base, +}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "aarch64-unknown-linux-pauthtest".into(), + metadata: TargetMetadata { + description: Some("ARM64 Linux with pauth enabled musl".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: Arch::AArch64, + + options: TargetOptions { + env: Env::Musl, + cfg_abi: CfgAbi::Pauthtest, + llvm_abiname: LlvmAbi::Pauthtest, + // `pauthtest` requires v8.3a, which includes lse, no need for outline-atomics + features: "+v8.3a,+pauth".into(), + max_atomic_width: Some(128), + stack_probes: StackProbeType::Inline, + crt_static_default: false, + crt_static_allows_dylibs: false, + // the AAPCS64 expects use of non-leaf frame pointers per + // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer + // and we tend to encounter interesting bugs in AArch64 unwinding code if we do not + frame_pointer: FramePointer::NonLeaf, + link_self_contained: LinkSelfContainedDefault::False, + mcount: "\u{1}_mcount".into(), + ..base::linux::opts() + }, + } +} diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 019d5629d6d6e..549d3ec1313c6 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -89,6 +89,34 @@ const UNWIND_DATA_REG: (i32, i32) = (10, 11); // x10, x11 #[cfg(any(target_arch = "loongarch32", target_arch = "loongarch64"))] const UNWIND_DATA_REG: (i32, i32) = (4, 5); // a0, a1 +unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { + cfg_select! { + all(target_abi = "pauthtest", target_arch = "aarch64") => { + // DWARF register number for SP on AArch64. + const SP_REG: i32 = 31; + + unsafe { + let sp = uw::_Unwind_GetGR(context, SP_REG).addr() as u64; + let mut addr = lpad.addr(); + + // `pacib` corresponds to `ptrauth_key_process_dependent_code` in . + core::arch::asm!( + "pacib {addr}, {sp}", + addr = inout(reg) addr, + sp = in(reg) sp, + options(nostack, preserves_flags) + ); + + lpad.with_addr(addr) + } + } + _ => { + let _ = context; + lpad + } + } +} + // The following code is based on GCC's C and C++ personality routines. For reference, see: // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c @@ -239,7 +267,8 @@ cfg_select! { exception_object.cast(), ); uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); + let maybe_signed_lpad = sign_lpad(context, lpad); + uw::_Unwind_SetIP(context, maybe_signed_lpad); uw::_URC_INSTALL_CONTEXT } EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index dad1ea6c57377..6cc9303f5eea1 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -13,10 +13,15 @@ fn main() { fn parent() { let me = env::current_exe().unwrap(); + // If `runner` is set up for current target, we'll be executing `./runner ./test`, not + // just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without actual executable. + let args = env::args(); let (rx, tx) = pipe().unwrap(); assert!( process::Command::new(me) + .args(args) .env("I_AM_THE_CHILD", "1") .stdout(tx) .status() diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 93f73ccad3ea4..b1973b54d7670 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -26,8 +26,16 @@ fn issue_15149() { env::join_paths(paths).unwrap() }; - let child_output = - process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap(); + // If `runner` is set up for current target, we'll be executing `./runner ./test`, not + // just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without actual executable. + let args = env::args(); + let child_output = process::Command::new("mytest") + .args(args) + .env("PATH", &path) + .arg("child") + .output() + .unwrap(); assert!( child_output.status.success(), diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 22568d5f6f1f9..071e9bead456f 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -57,7 +57,12 @@ cfg_select! { } } -#[cfg(target_env = "musl")] +// For pauthtest the only supported unwinding mechanism is provided by libunwind. +#[cfg(target_abi = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + +#[cfg(all(target_env = "musl", not(target_abi = "pauthtest")))] cfg_select! { all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 25b9d74417bba..6bcd3fb7a6633 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -10,6 +10,7 @@ use std::collections::HashSet; use std::env::split_paths; use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; +use std::process::Command; use std::{env, fs, iter}; use build_helper::exit; @@ -2285,7 +2286,13 @@ Please disable assertions with `rust.debug-assertions = false`. "-Lnative={}", builder.test_helpers_out(test_compiler.host).display() )); - targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display())); + let target_helpers = builder.test_helpers_out(target); + targetflags.push(format!("-Lnative={}", target_helpers.display())); + if target.is_pauthtest() { + // For the pauthtest target, embed an rpath to the directory containing the helper + // dynamic library. + targetflags.push(format!("-Clink-arg=-Wl,-rpath,{}", target_helpers.display())); + } } for flag in hostflags { @@ -3916,32 +3923,54 @@ impl Step for TestHelpers { }; let dst = builder.test_helpers_out(target); let src = builder.src.join("tests/auxiliary/rust_test_helpers.c"); - if up_to_date(&src, &dst.join("librust_test_helpers.a")) { - return; - } - let _guard = builder.msg_unstaged(Kind::Build, "test helpers", target); t!(fs::create_dir_all(&dst)); - let mut cfg = cc::Build::new(); - // We may have found various cross-compilers a little differently due to our - // extra configuration, so inform cc of these compilers. Note, though, that - // on MSVC we still need cc's detection of env vars (ugh). - if !target.is_msvc() { - if let Some(ar) = builder.ar(target) { - cfg.archiver(ar); + if !up_to_date(&src, &dst.join("librust_test_helpers.a")) { + let mut cfg = cc::Build::new(); + + // We may have found various cross-compilers a little differently due to our + // extra configuration, so inform cc of these compilers. Note, though, that + // on MSVC we still need cc's detection of env vars (ugh). + if !target.is_msvc() { + if let Some(ar) = builder.ar(target) { + cfg.archiver(ar); + } + cfg.compiler(builder.cc(target)); + } + cfg.cargo_metadata(false) + .out_dir(&dst) + .target(&target.triple) + .host(&builder.config.host_target.triple) + .opt_level(0) + .warnings(false) + .debug(false) + .file(builder.src.join("tests/auxiliary/rust_test_helpers.c")) + .compile("rust_test_helpers"); + } + if target.is_pauthtest() { + let so = dst.join("librust_test_helpers.so"); + if up_to_date(&src, &so) { + return; + } + + let status = Command::new(builder.cc(target)) + .arg("-target") + .arg(target.triple) + .arg("-march=armv8.3-a+pauth") + .arg("-fPIC") + .arg("-shared") + .arg("-O0") // Use O0 to match what static library is compiled at. + .arg("-o") + .arg(&so) + .arg(&src) + .status() + .unwrap_or_else(|_| panic!("Failed to run clang for {} toolchain", target.triple)); + + if !status.success() { + panic!("Linking of librust_test_helpers.so failed (target: {})", target.triple); } - cfg.compiler(builder.cc(target)); } - cfg.cargo_metadata(false) - .out_dir(&dst) - .target(&target.triple) - .host(&builder.config.host_target.triple) - .opt_level(0) - .warnings(false) - .debug(false) - .file(builder.src.join("tests/auxiliary/rust_test_helpers.c")) - .compile("rust_test_helpers"); } } diff --git a/src/bootstrap/src/core/config/target_selection.rs b/src/bootstrap/src/core/config/target_selection.rs index 8457607b897dd..365d6eff28ccf 100644 --- a/src/bootstrap/src/core/config/target_selection.rs +++ b/src/bootstrap/src/core/config/target_selection.rs @@ -78,6 +78,10 @@ impl TargetSelection { self.contains("msvc") } + pub fn is_pauthtest(&self) -> bool { + self.contains("pauthtest") + } + pub fn is_windows(&self) -> bool { self.contains("windows") } diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index ca8af279b92bc..85ae120550891 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -21,7 +21,7 @@ use crate::builder::Kind; use crate::core::build_steps::tool; use crate::core::config::{CompilerBuiltins, Target}; use crate::utils::exec::command; -use crate::{Build, Subcommand}; +use crate::{Build, Subcommand, t}; pub struct Finder { cache: HashMap>, @@ -37,6 +37,7 @@ pub struct Finder { /// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets. const STAGE0_MISSING_TARGETS: &[&str] = &[ // just a dummy comment so the list doesn't get onelined + "aarch64-unknown-linux-pauthtest", // Stage 0 compiler is not guaranteed to see the target yet. ]; /// Minimum version threshold for libstdc++ required when using prebuilt LLVM @@ -411,6 +412,53 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake { cmd_finder.must_have("wasm-component-ld"); } + + // aarch64-unknown-linux-pauthtest must use clang + if !skip_tools_checks && target.is_pauthtest() { + let cc_tool = build.cc_tool(*target); + let linker_path = build + .linker(*target) + .unwrap_or_else(|| panic!("{} requires an explicit clang linker", target.triple)); + + if !cc_tool.is_like_clang() { + panic!( + "Clang is required to build C code for {} target, got:\n\ + cc tool: `{}`,\n\ + linker: `{}`\n", + target.triple, + cc_tool.path().display(), + linker_path.display(), + ); + } + let cc_canon = t!(fs::canonicalize(cc_tool.path())); + let linker_canon = t!(fs::canonicalize(&linker_path)); + if cc_canon != linker_canon { + panic!( + "CC and Linker are expected to be the same for {} target, got:\n\ + CC: `{}`,\n\ + Linker: `{}`\n", + target.triple, + cc_canon.display(), + linker_canon.display(), + ); + } + + let output = + command(cc_tool.path()).arg("-dumpversion").run_capture_stdout(&build).stdout(); + let version_str = output.trim(); + let mut parts = version_str.split('.').map(|s| s.parse::().unwrap_or(0)); + let major = parts.next().unwrap_or(0); + let minor = parts.next().unwrap_or(0); + let patch = parts.next().unwrap_or(0); + if (major, minor, patch) < (22, 1, 0) { + panic!( + "clang version too old: {} ({} target trequires >= 22.1.0), path: {}", + target.triple, + version_str, + cc_tool.path().display() + ); + } + } } if let Some(ref s) = build.config.ccache { diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index cc10f476780c5..591ab929fdf67 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -49,6 +49,7 @@ - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-gnu](platform-support/aarch64-unknown-linux-gnu.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) + - [aarch64-unknown-linux-pauthtest](platform-support/aarch64-unknown-linux-pauthtest.md) - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md) - [aarch64v8r-unknown-none*](platform-support/aarch64v8r-unknown-none.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 26dd6b31b8991..a8825ccd12ba3 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -266,6 +266,7 @@ target | std | host | notes [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit [`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos `aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI) +[`aarch64-unknown-linux-pauthtest`](platform-support/aarch64-unknown-linux-pauthtest.md) | ✓ | ✓ | ARM64 PAC ELF ABI [`aarch64-unknown-managarm-mlibc`](platform-support/managarm.md) | ? | | ARM64 Managarm [`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? | | ARM64 QNX Neutrino 7.0 RTOS | 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 new file mode 100644 index 0000000000000..89acecf4b65ec --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-pauthtest.md @@ -0,0 +1,494 @@ +# aarch64-unknown-linux-pauthtest + +**Tier: 3** + +This target enables Pointer Authentication Code (PAC) support in Rust on AArch64 +ELF-based Linux systems. It uses the `aarch64-unknown-linux-pauthtest` LLVM +target and a pointer-authentication-enabled sysroot with a custom musl as a +reference libc implementation. Dynamic linking is required, with a dynamic +linker acting as the ELF interpreter that can resolve pauth relocations and +enforce pointer authentication constraints. + +Supported features include: +* authentication of signed function pointers for extern "C" calls (corresponds + to LLVM's `-fptrauth-calls`) +* signing of return addresses before spilling to the stack and authentication + after restoring for non-leaf functions (corresponds to `-fptrauth-returns`) +* trapping on authentication failure when the FPAC feature is not present + (corresponds to `-fptrauth-auth-traps`) +* signing of init/fini array entries using the LLVM-defined pointer + authentication scheme (corresponds to `-fptrauth-init-fini` and + `-fptrauth-init-fini-address-discrimination`) +* non-ABI-affecting indirect control-flow hardening features as implemented in + LLVM (corresponds to `-faarch64-jump-table-hardening` and + `-fptrauth-indirect-gotos`) +* signed ELF GOT entries (gated behind `-Z ptrauth-elf-got`, off by default) + +A tracking issue for adding support for the AArch64 pointer authentication ABI +in Rust can be found at +[#148640](https://github.com/rust-lang/rust/issues/148640). + +Existing compiler support, such as enabling branch authentication instructions +(i.e.: `-Z branch-protection`) provide limited functionality, mainly signing +return addresses (`pac-ret`). The new target goes further by enabling ABI-level +pointer authentication support. + +This target does not define a new ABI; it builds on the existing C/C++ language +ABI with pointer authentication support added. However, different authentication +features, encoded in the signing schema, are not ABI-compatible with one +another. + +Useful links: +* Clang pointer authentication documentation: + https://clang.llvm.org/docs/PointerAuthentication.html +* LLVM pointer authentication documentation: + https://llvm.org/docs/PointerAuth.html +* PAuth ABI Extension to ELF for the AArch64 architecture: + https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst + +## Target maintainers + +[@jchlanda](https://github.com/jchlanda) + +## Requirements + +This target supports cross-compilation from any Linux host, but execution +requires AArch64 with pointer authentication support (ARMv8.3-A or higher). + +## Standard library support + +Full std support is available: `core`, `alloc`, and `std` all build +successfully. All library tests (`core`, `alloc`, `std`) pass for this target as +well. + +## Building the toolchain + +Building this target requires a pointer-authentication-enabled sysroot based on +a custom musl toolchain. The sysroot must be available on the system before +compilation. To build it, follow the instructions in the [build scripts +repo](https://github.com/access-softek/pauth-toolchain-build-scripts). + +The target uses Clang, please make sure it is `v22.1.0` or higher. When using a +system-provided Clang, a compiler wrapper is required to supply the necessary +flags. Please consult the listing: + +```sh +#!/usr/bin/env sh + +clang \ + -target aarch64-unknown-linux-pauthtest \ + -march=armv8.3-a+pauth \ + --sysroot /aarch64-linux-pauthtest/usr \ + -resource-dir /lib/clang/ \ + --rtlib=compiler-rt \ + --ld-path=/usr/bin/ld.lld \ + --unwindlib=libunwind \ + -Wl,--dynamic-linker=/aarch64-linux-pauthtest/usr/lib/libc.so \ + -Wl,--rpath=/aarch64-linux-pauthtest/usr/lib \ + "$@" +``` + +Bootstrap validates the name of the configured C compiler, so when using a +wrapper its name must contain `clang`. A recommended name is +`aarch64-unknown-linux-pauthtest-clang`. Update the script to set `--sysroot`, +`-resource-dir`, `--dynamic-linker` and `--rpath` correctly by replacing +`` with the directory produced by the build scripts and the +`` with LLVM's version. Make the wrapper executable. + +To verify that the toolchain layout is correct, check that: +* the sysroot contains a pointer-authentication-enabled version of libunwind + (`/aarch64-linux-pauthtest/usr/lib/libunwind.so`), +* the Clang resource directory contains the appropriate `compiler-rt` objects + (`/lib/clang//lib/aarch64-unknown-linux-pauthtest/{clang_rt.crtbegin.o,clang_rt.crtend.o}`) + +When using the AccessSoftek scripts to build the sysroot, the result includes a +Clang-based toolchain. In this case, no wrapper script is required, +`/bin/aarch64-linux-pauthtest-clang` can be used directly. + +## Building the target + +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: +* `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: + +
+ +```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 ++++ b/src/bootstrap/Cargo.toml +@@ -94,3 +94,6 @@ debug = 0 + [profile.dev.package] + # Only use debuginfo=1 to further reduce compile times. + bootstrap.debug = 1 ++ ++[patch.crates-io] ++cc = { path = '/patches/cc-rs' } +``` + +
+ +In contrast to `cc-rs` and `libc`, which are external crates 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 +has not yet been committed there, which means an in-tree patch is currently +required. The patch: + +
+ +```diff +diff --git a/src/backtrace/libunwind.rs b/src/backtrace/libunwind.rs +index 0564f2e..a8a0d1a 100644 +--- a/src/backtrace/libunwind.rs ++++ b/src/backtrace/libunwind.rs +@@ -79,6 +79,18 @@ impl Frame { + // clause, and if this is fixed that test in theory can be run on macOS! + if cfg!(target_vendor = "apple") { + self.ip() ++ } else if cfg!(target_abi = "pauthtest") { ++ // NOTE: ip here is an unsigned (raw) pointer, so we must not use ++ // uw::_Unwind_FindEnclosingFunction. ++ // ++ // Otherwise, in the pointer-authentication-enabled reference ++ // toolchain, libunwind would attempt to authenticate and re-sign ++ // values. Performing signing here is not safe: it could create a ++ // signing oracle, and more importantly it is incorrect under the ++ // expected signing schema. ++ // The schema requires the stack pointer (SP) as the discriminator. ++ // However, the SP available at this point would not match the SP ++ // at authentication/re-sign time, since ++ // _Unwind_FindEnclosingFunction constructs a new unwind context. ++ // The SP used here would therefore correspond to a different frame. ++ // As a result, we must return the raw value. ++ self.ip() + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } + } +``` + +
+ +The target can be built by enabling it for a `rustc` build. + +```toml +[build] +target = ["aarch64-unknown-linux-pauthtest"] +``` + +Specify the binaries used by the target. + +```toml +[target.aarch64-unknown-linux-pauthtest] +cc = "/aarch64-unknown-linux-pauthtest-clang" +ar = "/llvm-ar" +ranlib = "/llvm-ranlib" +linker = "/aarch64-unknown-linux-pauthtest-clang" +``` + +Note that `cc` and `linker` must refer to the same binary (either Clang itself +or its wrapper). The bootstrap process will fail if they differ. On non-AArch64 +systems, ensure that QEMU is installed and that `binfmt_misc` is correctly +configured so that foreign architecture binaries can be executed transparently. + +## Building Rust programs + +Rust does not currently ship precompiled artifacts for this target. Programs +must be built using a locally compiled Rust toolchain, with +`aarch64-unknown-linux-pauthtest` target enabled. + +For a comprehensive example of how to interact between C and Rust programs +within the testing framework please consult +`/tests/run-make/pauth-quicksort-c-driver/rmake.rs`, the test builds +a C executable linked against Rust library. +`/tests/run-make/pauth-quicksort-rust-driver/rmake.rs` shows how to +link a Rust program against a library compiled from a C source file. + +### Minimal standalone Rust and C interoperability example + +A minimal standalone example demonstrating Rust and C interoperability on the +`aarch64-unknown-linux-pauthtest` target is listed below. + +
+ +* Project structure + +```text +rust_c_indirect/ + ┣━ Cargo.toml + ┣━ build.rs + ┣━ src/ + ┃ ┗━ main.rs + ┣━ c_src/ + ┃ ┗━ plugin.c + ┗━ target/ +``` + +* `Cargo.toml` + +```toml +[package] +name = "rust_c_indirect" +edition = "2024" +build = "build.rs" +``` + +* `build.rs` + +```rust, ignore (platform-specific) +use std::env; +use std::path::Path; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=c_src/plugin.c"); + + let clang = "/aarch64-unknown-linux-pauthtest-clang"; + + let out_dir = env::var("OUT_DIR").unwrap(); + let lib_path = Path::new(&out_dir).join("libplugin.so"); + let c_src = "c_src/plugin.c"; + + let status = Command::new(clang) + .args(["-shared", "-fPIC", c_src]) + .arg("-o") + .arg(&lib_path) + .status() + .unwrap_or_else(|_| panic!("failed to build shared library")); + assert!(status.success(), "failed to build shared library"); + + println!("cargo:rustc-link-arg=-Wl,--dynamic-linker=/aarch64-linux-pauthtest/usr/lib/libc.so"); + println!("cargo:rustc-link-arg=-Wl,-rpath,/aarch64-linux-pauthtest/usr/lib"); + println!("cargo:rustc-link-search=native={}", out_dir); + println!("cargo:rustc-link-lib=dylib=plugin"); +} + +``` + +* `src/main.rs` + +```rust, ignore (platform-specific) +use std::ptr; +use std::os::raw::c_int; + +unsafe extern "C" { + fn add(a: c_int, b: c_int) -> c_int; +} + +static OP: unsafe extern "C" fn(c_int, c_int) -> c_int = add; + +fn main() { + let a = 10; + let b = 32; + + let op = unsafe { ptr::read_volatile(&raw const OP) }; + let result = unsafe { op(a, b) }; + + println!("Result: {}", result); +} +``` + +* `c_src/plugin.c` + +```c +int add(int a, int b) { return a + b; } +``` + +
+ +* compile: `cargo build --target aarch64-unknown-linux-pauthtest --release` +* run: `./target/aarch64-unknown-linux-pauthtest/release/rust_c_indirect` + +Please make sure that `LD_LIBRARY_PATH` points to the directory containing +`libplugin.so`. For example: +`LD_LIBRARY_PATH=./target/aarch64-unknown-linux-pauthtest/release/build/rust_c_indirect-/out/`. + +To inspect pointer authentication behavior in IR, build with: +`RUSTFLAGS="--emit=llvm-ir"`. This generates an LLVM IR file, e.g.: +`target/aarch64-unknown-linux-pauthtest/release/deps/rust_c_indirect-*.ll`. +Relevant excerpt: + +```llvm +@_RNvCscVIHJvJIt8C_15rust_c_indirect2OP = internal constant ptr ptrauth (ptr @add, i32 0), align 8 + +%0 = load volatile ptr, ptr @_RNvCscVIHJvJIt8C_15rust_c_indirect2OP, align 8, !nonnull !5, !noundef !5 +%1 = tail call noundef i32 %0(i32 noundef 10, i32 noundef 32) #6 [ "ptrauth"(i32 0, i64 0) ] +``` + +Which shows that: +* function pointer (`@add`) is signed using `ptrauth`, when global variable is + initialized, +* the call is performed indirectly via a signed pointer, +* the `ptrauth` operand bundle enforces authentication at call time. + +Note, when building crates it is necessary to explicitly point Cargo to the +linker it has to use. This can be achieved by using a `config.toml` file (either +local to the project, or global), or by setting a +`CARGO_TARGET_AARCH64_UNKNOWN_LINUX_PAUTHTEST_LINKER` variable. For example: +* `.cargo/config.toml` + +```toml +[target.aarch64-unknown-linux-pauthtest] +linker = "/aarch64-unknown-linux-pauthtest-clang" +``` + +* `export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_PAUTHTEST_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. + +## Cross-compilation toolchains and C code + +This target supports interoperability with C code. A +pointer-authentication-enabled sysroot, built as described in the toolchain +build section of this document, is required. C code must be compiled with a +compiler configuration that supports pointer authentication. Mixed Rust/C +programs are supported and tested (e.g. quicksort examples). Pointer +authentication semantics must be consistent across Rust and C components. Only +dynamic linking is supported. + +The target can be cross-compiled from any Linux-based host, but execution +requires an AArch64 system that implements Pointer Authentication (PAC). In +practice, this means a CPU conforming to at least the Armv8.3-A architecture, +where the +[FEAT_PAuth](https://developer.arm.com/documentation/109697/2025_06/Feature-descriptions/The-Armv8-3-architecture-extension?lang=en#md448-the-armv83-architecture-extension__feat_FEAT_PAuth) +extension is defined. + +Cross-compilation has been successfully performed on both +`aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-gnu` hosts. + +## Testing + +This target can be tested as normal with `x.py`. +The following categories are supported (all present in tree): +* Assembly tests + * pauth-basic.rs +* LLVM IR/codegen tests + * pauth-extern-c.rs + * pauth-extern-c-direct-indirect-call.rs + * pauth-extern-weak-global.rs + * pauth-init-fini.rs + * pauth-attr-special-funcs.rs +* End-to-end execution tests + * Rust-driven quicksort (pauth-quicksort-rust-driver) + * C-driven quicksort (pauth-quicksort-c-driver) +* UI error/warning reporting + * crt-static-pauthtest.rs + * pauth-static-link-warning + * enable_pointer_authentication_validation.rs + * invalid_target_pointer_authentication.rs + * type_discrimination_not_supported_pointer_authentication.rs + +All tests from `assembly-llvm`, `codegen-llvm`, `codegen-units`, `coverage`, +`crashes`, `incremental`, `library`, `mir-opt`, `run-make`, `ui` and +`ui-fulldeps` subsets are expected to pass. + +Command to run all passing tests (with tests added by this target explicitly +named for convenience): + +```sh +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/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/ui/pointer_authentication/enable_pointer_authentication_validation.rs \ + tests/ui/pointer_authentication/invalid_target_pointer_authentication.rs \ + tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs +``` + +## Limitations + +Operand bundles should only be attached to indirect function calls. However, +function pointer signing is currently performed in `get_fn_addr`, which causes +the logic to be applied too broadly, including to function values (not just +pointers). As a result, direct calls using signed function values must also +receive operand bundles. Once this is resolved, we should analyze each call and +skip direct calls. +For more information please see the discussion in the [rust-lang issue +tracker](https://github.com/rust-lang/rust/issues/152532). + +The current version only supports C interoperability with pointer authentication +features explicitly mentioned at the beginning of this document. Further work is +needed to support configurable signing schemas (i.e. selection of signing keys, +discriminators, address diversity, and features opt-in/opt-out) as defined by +the LLVM pointer authentication model. + +C++ interoperability is not currently supported. Features such as signing C++ +member function pointers, virtual function pointers, and virtual table pointers +are not expected to work. diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index 41ea492d6f38f..f06b9b4d49d9f 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -105,6 +105,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "ignore-nvptx64-nvidia-cuda", "ignore-openbsd", "ignore-parallel-frontend", + "ignore-pauthtest", "ignore-powerpc", "ignore-powerpc64", "ignore-remote", @@ -246,6 +247,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "only-musl", "only-nightly", "only-nvptx64", + "only-pauthtest", "only-powerpc", "only-riscv32", "only-riscv64", diff --git a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs index 1177c1e68ed56..226f177dfb093 100644 --- a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs +++ b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs @@ -2,6 +2,9 @@ //@ compile-flags: -Copt-level=3 //@ only-aarch64 //@ only-linux +// aarch64-unknown-linux-pauthtest requires armv8.3-a, which includes Large System Extensions, +// providing hardware implementations of atomic operations. +//@ ignore-pauthtest #![crate_type = "rlib"] diff --git a/tests/assembly-llvm/pauth-basic.rs b/tests/assembly-llvm/pauth-basic.rs new file mode 100644 index 0000000000000..e240e1317f3a3 --- /dev/null +++ b/tests/assembly-llvm/pauth-basic.rs @@ -0,0 +1,44 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ only-pauthtest +//@ revisions: aarch64_unknown_linux_pauthtest +//@ [aarch64_unknown_linux_pauthtest] compile-flags: --target=aarch64-unknown-linux-pauthtest +//@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; + +#[no_mangle] +#[inline(never)] +pub extern "C" fn c_func(a: i32) -> i32 { + a +} + +#[no_mangle] +#[inline(never)] +fn call_through(f: extern "C" fn(i32) -> i32, x: i32) -> i32 { + f(x) +} + +#[no_mangle] +#[inline(never)] +pub fn call_c_func(x: i32) -> i32 { + call_through(c_func, x) +} + +// CHECK-LABEL: call_through: +// CHECK: mov [[PTR:x[0-9]+]], x0 +// CHECK: mov w0, w1 +// CHECK: braaz [[PTR]] + +// CHECK-LABEL: call_c_func: +// CHECK: adrp [[GOT_REG:x[0-9]+]], :got:c_func +// CHECK: ldr [[GOT_REG]], [[[GOT_REG]], :got_lo12:c_func] +// CHECK: paciza [[FN_REG:x[0-9]+]] +// CHECK: mov w1, w0 +// CHECK: mov x0, [[FN_REG]] +// CHECK: b call_through diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs index ce6629b4f27ce..6364e34069f09 100644 --- a/tests/assembly-llvm/targets/targets-elf.rs +++ b/tests/assembly-llvm/targets/targets-elf.rs @@ -55,6 +55,9 @@ //@ revisions: aarch64_unknown_linux_ohos //@ [aarch64_unknown_linux_ohos] compile-flags: --target aarch64-unknown-linux-ohos //@ [aarch64_unknown_linux_ohos] needs-llvm-components: aarch64 +//@ revisions: aarch64_unknown_linux_pauthtest +//@ [aarch64_unknown_linux_pauthtest] compile-flags: --target aarch64-unknown-linux-pauthtest +//@ [aarch64_unknown_linux_pauthtest] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_managarm_mlibc //@ [aarch64_unknown_managarm_mlibc] compile-flags: --target aarch64-unknown-managarm-mlibc //@ [aarch64_unknown_managarm_mlibc] needs-llvm-components: aarch64 diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index c949d8861975b..e50b9b785ad48 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -8,7 +8,10 @@ //! items. For identical error output, any `diagnostic` attributes (e.g. `on_unimplemented`) //! should also be replicated here. //! - Be careful of adding new features and things that are only available for a subset of targets. +//! - `Sync` is only provided such that the minimal set of impls required by tests is met (not +//! exhaustive covering of all possible function pointer signatures). //! + //! # References //! //! This is partially adapted from `rustc_codegen_cranelift`: @@ -299,6 +302,16 @@ impl_marker_trait!( impl Sync for () {} impl Sync for [T; N] {} +// 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 {} #[lang = "drop_glue"] fn drop_glue(_: &mut T) {} @@ -367,6 +380,16 @@ pub mod ptr { } } +pub mod hint { + #[inline] + pub fn black_box(dummy: T) -> T { + #[rustc_intrinsic] + fn black_box(dummy: T) -> T; + + unsafe { black_box(dummy) } + } +} + #[lang = "c_void"] #[repr(u8)] pub enum c_void { diff --git a/tests/codegen-llvm/box-uninit-bytes.rs b/tests/codegen-llvm/box-uninit-bytes.rs index 7ac929646cd4d..fc47e42d6f9db 100644 --- a/tests/codegen-llvm/box-uninit-bytes.rs +++ b/tests/codegen-llvm/box-uninit-bytes.rs @@ -43,4 +43,4 @@ pub fn box_lotsa_padding() -> Box { // from the CHECK-NOT above, and also verify the attributes got set reasonably. // CHECK: declare {{(dso_local )?}}noalias noundef ptr @{{.*}}__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef range(i{{[0-9]+}} 1, {{-2147483647|-9223372036854775807}})) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]] -// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) {{(uwtable )?}}"alloc-family"="__rust_alloc" {{.*}} } +// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned"){{.*}} allocsize(0) {{(uwtable )?}}{{.*}}"alloc-family"="__rust_alloc" {{.*}} } diff --git a/tests/codegen-llvm/cffi/c-variadic.rs b/tests/codegen-llvm/cffi/c-variadic.rs index 0fa63b42c2de6..3aaa311325f13 100644 --- a/tests/codegen-llvm/cffi/c-variadic.rs +++ b/tests/codegen-llvm/cffi/c-variadic.rs @@ -1,5 +1,9 @@ //@ needs-unwind //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 +// Pauthtest generates pointer authentication metadata for call instructions +// and wraps function pointers in ConstPtrAuth. Disable this test for this target +// to avoid clutter from pointer authentication complexity. +//@ ignore-pauthtest #![crate_type = "lib"] #![feature(c_variadic)] diff --git a/tests/codegen-llvm/inline-always-works-always.rs b/tests/codegen-llvm/inline-always-works-always.rs index 07200fd9e373a..e3dfbc819f9af 100644 --- a/tests/codegen-llvm/inline-always-works-always.rs +++ b/tests/codegen-llvm/inline-always-works-always.rs @@ -2,6 +2,8 @@ //@[NO-OPT] compile-flags: -Copt-level=0 //@[SIZE-OPT] compile-flags: -Copt-level=s //@[SPEED-OPT] compile-flags: -Copt-level=3 +// Pointer authenticated calls are not guaranteed to be inlined. +//@ ignore-pauthtest #![crate_type = "rlib"] diff --git a/tests/codegen-llvm/issues/issue-73258.rs b/tests/codegen-llvm/issues/issue-73258.rs index c9eceb0dccf73..4962c53853aac 100644 --- a/tests/codegen-llvm/issues/issue-73258.rs +++ b/tests/codegen-llvm/issues/issue-73258.rs @@ -3,6 +3,8 @@ #![crate_type = "lib"] // Adapted from +// We explicitly match against `call{{.*}}(` because the emitted call may carry attributes (e.g. +// `ptrauth-calls`), which would otherwise make a plain `call` pattern too permissive. #[derive(Clone, Copy)] #[repr(u8)] @@ -17,7 +19,7 @@ pub enum Foo { #[no_mangle] pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select @@ -25,14 +27,14 @@ pub unsafe fn issue_73258(ptr: *const Foo) -> Foo { // CHECK-SAME: !range ! // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select // CHECK: ret i8 %[[R]] // CHECK-NOT: icmp - // CHECK-NOT: call + // CHECK-NOT: call{{.*}}( // CHECK-NOT: br {{.*}} // CHECK-NOT: select let k: Option = Some(ptr.read()); 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..3fe8df0c1bdcc --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-attr-cli-flags.rs @@ -0,0 +1,118 @@ +// 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 + +//@[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]+]] +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 new file mode 100644 index 0000000000000..2751494b9de7a --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-attr-special-funcs.rs @@ -0,0 +1,31 @@ +//@ only-pauthtest +//@ compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +// Make sure that compiler generated functions (main wrapper and __rust_try) also have ptrauth +// attributes set correctly. Rustc only generates __rust_try at O0, so use that opt level for the +// test. + +//@ needs-llvm-components: aarch64 + +use std::panic; + +// CHECK: define {{.*}} @__rust_try{{.*}} [[ATTR_TRY:#[0-9]+]] +// CHECK: define {{.*}} @main{{.*}} [[ATTR_MAIN:#[0-9]+]] + +// CHECK: attributes [[ATTR_TRY]] = { {{.*}}"aarch64-jump-table-hardening" +// CHECK-DAG: "ptrauth-auth-traps" +// CHECK-DAG: "ptrauth-calls" +// CHECK-DAG: "ptrauth-indirect-gotos" +// CHECK-DAG: "ptrauth-returns" + +// CHECK: attributes [[ATTR_MAIN]] = { {{.*}}"aarch64-jump-table-hardening" +// 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"); + }); +} + +// CHECK: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs new file mode 100644 index 0000000000000..643b428339b73 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c-direct-indirect-call.rs @@ -0,0 +1,98 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 + +// Make sure that direct extern "C" calls are not handled by pointer authentication operand bundle +// logic. +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::hint::black_box; +use minicore::*; + +extern "C" { + fn rand() -> bool; + fn add(a: i32, b: i32) -> i32; + fn sub(a: i32, b: i32) -> i32; + + // Corresponds to: void *woof; + static mut woof: *mut c_void; + fn direct_function_taking_void_arg(data: *mut c_void); + fn direct_no_arg(); + fn direct_function_taking_fp_arg(func: unsafe extern "C" fn()); +} + +type CFnPtr = unsafe extern "C" fn(i32, i32) -> i32; + +// CHECK-LABE: test_indirect_call +#[inline(never)] +fn test_indirect_call() { + let fp_add: CFnPtr = black_box(add); + let fp_sub: CFnPtr = black_box(sub); + + unsafe { + let a = black_box(fp_add); + let b = black_box(fp_sub); + + // O0_PAUTH: call i32 %{{.*}}(i32 7, i32 4) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call noundef i32 %{{.*}}(i32 noundef 7, i32 noundef 4) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id1 = a(7, 4); + + // O0_PAUTH: call i32 %{{.*}}(i32 10, i32 6) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call noundef i32 %{{.*}}(i32 noundef 10, i32 noundef 6) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id2 = b(10, 6); + } + + // Also test calling via conditional pointer + unsafe { + // O0_PAUTH: call {{.*}}i1 ptrauth (ptr @rand, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i1 @rand() # + let use_add = rand(); + // O0_PAUTH: store ptr ptrauth (ptr @sub, i32 0), ptr %[[FP_O0:[a-zA-Z0-9_.]+]] + // O0_PAUTH: store ptr ptrauth (ptr @add, i32 0), ptr %[[FP_O0]]{{.*}} + // O0_PAUTH: %[[LOAD_FP_O0:[a-zA-Z0-9_.]+]] = load ptr, ptr %[[FP_O0]]{{.*}} + // O3_PAUTH: %[[FP_O3:.*]] = select i1 %{{.*}}, ptr ptrauth (ptr @add, i32 0), ptr ptrauth (ptr @sub, i32 0) + let fp: CFnPtr = if use_add { add } else { sub }; + // O0_PAUTH: call i32 %[[LOAD_FP_O0]](i32 1, i32 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 %[[FP_O3]](i32 noundef 1, i32 noundef 2) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + let _id3 = fp(1, 2); + } + + unsafe { + direct_function_taking_fp_arg(direct_no_arg); + } +} + +// CHECK-LABEL: test_direct_call +#[inline(never)] +fn test_direct_call() { + unsafe { + // O0_PAUTH: call {{.*}}i32 ptrauth (ptr @add, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 @add(i32 {{.*}}2, i32 {{.*}}3) # + let _d1 = add(2, 3); + // O0_PAUTH: call {{.*}}i32 ptrauth (ptr @sub, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call {{.*}}i32 @sub(i32 {{.*}}5, i32 {{.*}}1) # + let _d2 = sub(5, 1); + + // O0_PAUTH: call {{.*}}void ptrauth (ptr @direct_function_taking_void_arg, i32 0)({{.*}}) #{{[0-9]+}} [ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: {{(tail )?}}call void @direct_function_taking_void_arg(ptr noundef %{{.*}}) # + direct_function_taking_void_arg(woof); + } +} + +pub fn entry() { + test_indirect_call(); + test_direct_call(); +} + +// O0_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-c.rs b/tests/codegen-llvm/pauth/pauth-extern-c.rs new file mode 100644 index 0000000000000..5b0253a7bbe60 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-c.rs @@ -0,0 +1,75 @@ +// ignore-tidy-linelength +//@ only-pauthtest +//@ add-minicore + +//@ revisions: O0_PAUTH O3_PAUTH O0_PAUTH-ELF-GOT O3_PAUTH-ELF-GOT O0_NO_PAUTH O3_NO_PAUTH + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [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 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 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 +//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] + +extern crate minicore; + +type FnPtr = unsafe extern "C" fn(i32, i32) -> i32; +// O0_NO_PAUTH-NOT: "ptrauth"(i32 +// O3_NO_PAUTH-NOT: "ptrauth"(i32 + +// O0_PAUTH: define {{.*}}test_entry +// O3_PAUTH: define {{.*}}test_entry +#[no_mangle] +pub unsafe extern "C" fn test_entry(x: usize) { + // O0_PAUTH: call{{.*}}_RNvCshUtaFcP1mZ5_14pauth_extern_c7call_it(ptr ptrauth (ptr @external_c_callee, i32 0), i32 5, i32 7) + // O3_PAUTH: call{{.*}}_RNvCshUtaFcP1mZ5_14pauth_extern_c7call_it(ptr{{.*}}ptrauth (ptr @external_c_callee, i32 0), i32{{.*}}5, i32{{.*}}7) + let _ = call_it(external_c_callee, 5, 7); +} + +// O0_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O0_1:[0-9]+]] +// O3_PAUTH: define {{.*}}pauth_extern_c7call_it{{.*}} #[[ATTR_O3_1:[0-9]+]] +#[inline(never)] +pub fn call_it(fn_ptr: FnPtr, arg_1: i32, arg_2: i32) -> i32 { + // O0_PAUTH: call i32 %fn_ptr(i32 %arg_1, i32 %arg_2){{.*}}[ "ptrauth"(i32 0, i64 0) ] + // O3_PAUTH: call{{.*}}i32 %fn_ptr(i32{{.*}}%arg_1, i32{{.*}}%arg_2){{.*}}[ "ptrauth"(i32 0, i64 0) ] + unsafe { fn_ptr(arg_1, arg_2) } +} + +extern "C" { + fn external_c_callee(a: i32, b: i32) -> i32; +} + +// O0_PAUTH-CHECK: attributes #[[ATTR_O0_1]] = { {{.*}}"aarch64-jump-table-hardening" +// O0_PAUTH-CHECK-SAME: "ptrauth-auth-traps" +// O0_PAUTH-CHECK-SAME: "ptrauth-calls" +// O0_PAUTH-CHECK-SAME: "ptrauth-indirect-gotos" +// O0_PAUTH-CHECK-SAME: "ptrauth-returns" + +// O3_PAUTH-CHECK: attributes #[[ATTR_O3_1]] = { {{.*}}"aarch64-jump-table-hardening" +// O3_PAUTH-CHECK-SAME: "ptrauth-auth-traps" +// O3_PAUTH-CHECK-SAME: "ptrauth-calls" +// O3_PAUTH-CHECK-SAME: "ptrauth-indirect-gotos" +// O3_PAUTH-CHECK-SAME: "ptrauth-returns" + +// O0_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_PAUTH-ELF-GOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_PAUTH: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} + +// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O0_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} +// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-elf-got", i32 1} +// O3_NO_PAUTH-NOT: !{{[0-9]+}} = !{i32 1, !"ptrauth-sign-personality", i32 1} diff --git a/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs new file mode 100644 index 0000000000000..a83298dd5725c --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-extern-weak-global.rs @@ -0,0 +1,38 @@ +// ignore-tidy-linelength +//@ only-pauthtest +//@ revisions: O0_PAUTH O3_PAUTH O0_NO_PAUTH O3_NO_PAUTH +//@ add-minicore + +//@ [O0_PAUTH] needs-llvm-components: aarch64 +//@ [O0_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=0 +//@ [O3_PAUTH] needs-llvm-components: aarch64 +//@ [O3_PAUTH] compile-flags: --target=aarch64-unknown-linux-pauthtest -C opt-level=3 +//@ [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 +//@ [O3_NO_PAUTH] compile-flags: --target=aarch64-unknown-linux-gnu -C opt-level=3 + +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![feature(no_core)] +#![feature(linkage)] + +extern crate minicore; +use minicore::*; + +// O0_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0) +// O0_PAUTH: declare i64 @extern_weak_fn({{.*}}) +// O3_PAUTH: @{{[0-9A-Za-z_]+}}FUNCTION_PTR_DECL = constant ptr ptrauth (ptr @extern_weak_fn, i32 0) +// O3_PAUTH: declare {{.*}} i64 @extern_weak_fn({{.*}}) +// +// O0_NO_PAUTH-NOT: ptr ptrauth +// O3_NO_PAUTH-NOT: ptr ptrauth +extern "C" { + #[link_name = "extern_weak_fn"] + #[linkage = "extern_weak"] + fn extern_weak_fn() -> i64; +} + +#[used] +static FUNCTION_PTR_DECL: unsafe extern "C" fn() -> i64 = extern_weak_fn; diff --git a/tests/codegen-llvm/pauth/pauth-init-fini.rs b/tests/codegen-llvm/pauth/pauth-init-fini.rs new file mode 100644 index 0000000000000..d457973b690f2 --- /dev/null +++ b/tests/codegen-llvm/pauth/pauth-init-fini.rs @@ -0,0 +1,51 @@ +//@ add-minicore +// ignore-tidy-linelength +//@ only-pauthtest +//@ 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] 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 +//@ [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 + +// Make sure that init/fini metadata uses correct discriminator: 0xd9d4/55764 - ptrauth_string_discriminator("init_fini"). +// And that address discriminator can be enabled. + +#![feature(no_core, lang_items)] +#![no_std] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +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 +#[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 +#[used] +#[link_section = ".fini_array.90"] +static GLOBAL_FINI: extern "C" fn(i32) = fini_fn; + +extern "C" fn init_fn() {} +extern "C" fn fini_fn(_: i32) {} diff --git a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs index 96b8ade1f1b16..7be68faccc714 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/rmake.rs @@ -6,6 +6,8 @@ //@ needs-target-std //@ ignore-android: FIXME(#142855) //@ ignore-sgx: (x86 machine code cannot be directly executed) +//@ ignore-pauthtest: (it requires non-trivial compilation of c sources, and only supports dynamic +// linking, ignore the test). use run_make_support::{cc, extra_c_flags, run, rustc, static_lib_name}; diff --git a/tests/run-make/pauth-quicksort-c-driver/main.c b/tests/run-make/pauth-quicksort-c-driver/main.c new file mode 100644 index 0000000000000..58f8d149cc446 --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/main.c @@ -0,0 +1,45 @@ +#include +#include + +#define NUM_ELEMS 5 + +void quickSort(void *Base, size_t N, size_t Size, + int (*Cmp)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif + +int cmpI32Ascending(const void *LHS, const void *RHS) { + int32_t x = *(const int32_t *)LHS; + int32_t y = *(const int32_t *)RHS; + + if (x < y) + return -1; + else if (x > y) + return 1; + else + return 0; +} + +int main() { + int32_t Data[NUM_ELEMS] = {4, 2, 5, 3, 1}; + + printf("Before sorting: "); + for (int i = 0; i < NUM_ELEMS; i++) + printf("%d ", Data[i]); + printf("\n"); + + quickSort(Data, NUM_ELEMS, sizeof(int32_t), cmpI32Ascending); + + printf("After sorting: "); + for (int i = 0; i < NUM_ELEMS; i++) + printf("%d ", Data[i]); + printf("\n"); + + for (size_t i = 1; i < NUM_ELEMS; i++) + if (Data[i - 1] > Data[i]) + return 42; + + return 0; +} diff --git a/tests/run-make/pauth-quicksort-c-driver/quicksort.rs b/tests/run-make/pauth-quicksort-c-driver/quicksort.rs new file mode 100644 index 0000000000000..ac73dced2e1fc --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/quicksort.rs @@ -0,0 +1,66 @@ +use std::mem::size_of; +use std::os::raw::{c_int, c_void}; +use std::ptr; + +unsafe fn swap_i32(lhs: *mut i32, rhs: *mut i32) { + ptr::swap(lhs, rhs); +} + +unsafe fn partition( + arr: *mut i32, + low: isize, + high: isize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) -> isize { + let pivot = arr.offset(low); + let mut i = low; + let mut j = high; + + while i < j { + while i <= high - 1 && cmp(arr.offset(i) as *const c_void, pivot as *const c_void) <= 0 { + i += 1; + } + + while j >= low + 1 && cmp(arr.offset(j) as *const c_void, pivot as *const c_void) > 0 { + j -= 1; + } + + if i < j { + swap_i32(arr.offset(i), arr.offset(j)); + } + } + + swap_i32(arr.offset(low), arr.offset(j)); + j +} + +unsafe fn quicksort_rec( + arr: *mut i32, + low: isize, + high: isize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + if low < high { + let part = partition(arr, low, high, cmp); + quicksort_rec(arr, low, part - 1, cmp); + quicksort_rec(arr, part + 1, high, cmp); + } +} + +#[no_mangle] +pub extern "C" fn quickSort( + base: *mut c_void, + n: usize, + size: usize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, +) { + if size != size_of::() { + std::process::abort(); + } + + if n > 1 { + unsafe { + quicksort_rec(base as *mut i32, 0, (n as isize) - 1, cmp); + } + } +} diff --git a/tests/run-make/pauth-quicksort-c-driver/rmake.rs b/tests/run-make/pauth-quicksort-c-driver/rmake.rs new file mode 100644 index 0000000000000..3cf978a2f6da5 --- /dev/null +++ b/tests/run-make/pauth-quicksort-c-driver/rmake.rs @@ -0,0 +1,42 @@ +// Test compilation flow using custom pauth-enabled toolchain and signing extern "C" function +// pointers used from within rust. The test assumes that pointer-authentication-enabled `clang` is +// available on the path. In this test rust is the driver - providing the data and the comparison +// function; while c - provides the implementation of quicksort algorithm and is the user of the +// data and comparator. + +//@ only-pauthtest + +use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; + +fn main() { + // Use CC and CC_DEFAULT_FLAGS env variables to set up linker for rustc. This results in the + // same command as cc(). The CC env variable corresponds to cc field in the config toml file. + // This field is required to point to a clang family compiler on aarch64-unknown-linux-pauthtest + // target. + let rust_lib_name = "rust_quicksort"; + rustc() + .target("aarch64-unknown-linux-pauthtest") + .crate_type("cdylib") + .input("quicksort.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .crate_name(rust_lib_name) + .run(); + + let exe_name = "main"; + cc().out_exe(exe_name) + .input("main.c") + .args(&[ + "-march=armv8.3-a+pauth", + "-target", + "aarch64-unknown-linux-pauthtest", + &format!("-l{}", rust_lib_name), + ]) + .library_search_path(".") + .run(); + + run(exe_name); + + rfs::remove_file(format!("{}{rust_lib_name}.{}", "lib", "so")); + run_fail(exe_name); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/main.rs b/tests/run-make/pauth-quicksort-rust-driver/main.rs new file mode 100644 index 0000000000000..37700e0ddc169 --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/main.rs @@ -0,0 +1,43 @@ +use std::os::raw::{c_int, c_void}; + +#[link(name = "quicksort")] +extern "C" { + fn quickSort( + base: *mut c_void, + n: usize, + size: usize, + cmp: extern "C" fn(*const c_void, *const c_void) -> c_int, + ); +} + +extern "C" fn cmp_i32_ascending(a: *const c_void, b: *const c_void) -> c_int { + unsafe { + let x = *(a as *const i32); + let y = *(b as *const i32); + + if x < y { + -1 + } else if x > y { + 1 + } else { + 0 + } + } +} + +fn main() { + let mut data: [i32; 5] = [4, 2, 5, 3, 1]; + println!("Before sorting: {:?}", data); + + unsafe { + quickSort( + data.as_mut_ptr() as *mut c_void, + data.len(), + std::mem::size_of::(), + cmp_i32_ascending, + ); + } + + println!("After sorting: {:?}", data); + assert!(data.windows(2).all(|w| w[0] <= w[1])); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/quicksort.c b/tests/run-make/pauth-quicksort-rust-driver/quicksort.c new file mode 100644 index 0000000000000..029435d562bb6 --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/quicksort.c @@ -0,0 +1,50 @@ +#include +#include +#include + +void swap(void *A, void *B, size_t Size) { + unsigned char Tmp[Size]; + memcpy(Tmp, A, Size); + memcpy(A, B, Size); + memcpy(B, Tmp, Size); +} + +int partition(void *Base, int Low, int High, size_t Size, + int (*Cmp)(const void *, const void *)) { + char *Arr = (char *)Base; + + void *Pivot = Arr + Low * Size; + int i = Low; + int j = High; + + while (i < j) { + while (i <= High - 1 && Cmp(Arr + i * Size, Pivot) <= 0) + i++; + + while (j >= Low + 1 && Cmp(Arr + j * Size, Pivot) > 0) + j--; + + if (i < j) + swap(Arr + i * Size, Arr + j * Size, Size); + } + + swap(Arr + Low * Size, Arr + j * Size, Size); + return j; +} + +void quickSortRec(void *Base, int Low, int High, size_t Size, + int (*Cmp)(const void *, const void *)) { + if (Low < High) { + int Part = partition(Base, Low, High, Size, Cmp); + quickSortRec(Base, Low, Part - 1, Size, Cmp); + quickSortRec(Base, Part + 1, High, Size, Cmp); + } +} + +void quickSort(void *Base, size_t N, size_t Size, + int (*Cmp)(const void *, const void *)) { + if (Size != sizeof(int32_t)) + abort(); + if (N > 1) + quickSortRec(Base, 0, (int)N - 1, Size, Cmp); +} diff --git a/tests/run-make/pauth-quicksort-rust-driver/rmake.rs b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs new file mode 100644 index 0000000000000..e77081d70039c --- /dev/null +++ b/tests/run-make/pauth-quicksort-rust-driver/rmake.rs @@ -0,0 +1,34 @@ +// Test compilation flow using custom pauth-enabled toolchain and signing extern "C" function +// pointers used from within rust. The test assumes that pointer-authentication-enabled `clang` is +// available on the path. +// In this test rust is the driver - providing the data and the comparison function; while c - +// provides the implementation of quicksort algorithm and is the user of the data and comparator. + +//@ only-pauthtest + +use run_make_support::{cc, env_var, rfs, run, run_fail, rustc}; + +fn main() { + let input = "quicksort"; + let input_name = format!("{input}.c"); + let lib_name = format!("{}{input}.{}", "lib", "so"); + cc().out_exe(&lib_name) + .input(&input_name) + .args(&["-target", "aarch64-unknown-linux-pauthtest", "-march=armv8.3-a+pauth", "-shared"]) + .run(); + + // Use CC and CC_DEFAULT_FLAGS env variables to set up linker for rustc. This results in the + // same command as cc(). The CC env variable corresponds to cc field in the config toml file. + // This field is required to point to a clang family compiler on aarch64-unknown-linux-pauthtest + // target. + rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .run(); + run("main"); + + rfs::remove_file(&lib_name); + run_fail("main"); +} diff --git a/tests/run-make/pauth-static-link-warning/helper.c b/tests/run-make/pauth-static-link-warning/helper.c new file mode 100644 index 0000000000000..fb2bfd5cb990e --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/helper.c @@ -0,0 +1 @@ +int helper_function() { return 42; } diff --git a/tests/run-make/pauth-static-link-warning/main.rs b/tests/run-make/pauth-static-link-warning/main.rs new file mode 100644 index 0000000000000..0323db8838559 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/main.rs @@ -0,0 +1,10 @@ +#[link(name = "helper", kind = "static")] +extern "C" { + fn helper_function() -> i32; +} + +fn main() { + unsafe { + assert!(42 == helper_function()); + } +} diff --git a/tests/run-make/pauth-static-link-warning/main_cmd_line.rs b/tests/run-make/pauth-static-link-warning/main_cmd_line.rs new file mode 100644 index 0000000000000..a0edd66ce9910 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/main_cmd_line.rs @@ -0,0 +1,9 @@ +extern "C" { + fn helper_function() -> i32; +} + +fn main() { + unsafe { + assert!(42 == helper_function()); + } +} diff --git a/tests/run-make/pauth-static-link-warning/rmake.rs b/tests/run-make/pauth-static-link-warning/rmake.rs new file mode 100644 index 0000000000000..8d4fb18fe33f9 --- /dev/null +++ b/tests/run-make/pauth-static-link-warning/rmake.rs @@ -0,0 +1,46 @@ +// Make sure that for `aarch64-unknown-linux-pauthtest` compiler emits warning when static +// libraries are linked. Test both foreign module linked from #[link] directive and command line +// invocations. + +//@ only-pauthtest +// ignore-tidy-linelength + +use run_make_support::{cc, env_var, regex, run, rustc}; + +fn main() { + let input = "helper"; + let input_name = format!("{input}.c"); + let lib_name = format!("{}{input}.{}", "lib", "a"); + // Build a static library + cc().out_exe(&lib_name) + .input(&input_name) + .args(&["-target", "aarch64-unknown-linux-pauthtest", "-march=armv8.3-a+pauth", "-c"]) + .run(); + + // Check against foreign module warning: #[link(name = "helper", kind = "static")] + let stderr_foreign_module = rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .arg("-L.") + .run() + .stderr_utf8(); + run("main"); + let re_foreign_moule = regex::Regex::new( r"(?s)warning: library `helper`.*linked statically.*aarch64-unknown-linux-pauthtest.*requires dynamic linking.*using dynamic linking instead").unwrap(); + assert!(re_foreign_moule.is_match(&stderr_foreign_module)); + + // Check against command line warning: -lstatic=helper + let stderr_command_line = rustc() + .target("aarch64-unknown-linux-pauthtest") + .input("main_cmd_line.rs") + .linker(&env_var("CC")) + .link_args(&env_var("CC_DEFAULT_FLAGS")) + .arg("-L.") + .arg("-lstatic=helper") + .run() + .stderr_utf8(); + run("main_cmd_line"); + let re_cmd_line = regex::Regex::new( r"(?s)warning: static linking of `helper`.*is not supported on.*aarch64-unknown-linux-pauthtest.*using dynamic linking instead").unwrap(); + assert!(re_cmd_line.is_match(&stderr_command_line)); +} diff --git a/tests/run-make/rustdoc-test-builder/rmake.rs b/tests/run-make/rustdoc-test-builder/rmake.rs index 17d40c68fd922..5520426e16fa8 100644 --- a/tests/run-make/rustdoc-test-builder/rmake.rs +++ b/tests/run-make/rustdoc-test-builder/rmake.rs @@ -27,6 +27,7 @@ fn main() { // so only exercise the success path when the target can run on the host. if target().contains("wasm") || target().contains("sgx") + || target().contains("pauthtest") || std::env::var_os("REMOTE_TEST_CLIENT").is_some() { return; diff --git a/tests/ui/README.md b/tests/ui/README.md index f76cd507056fe..443d48182058f 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1079,6 +1079,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/check-cfg/well-known-values.stderr b/tests/ui/check-cfg/well-known-values.stderr index 47ed9891a1a6c..ebcb439b73498 100644 --- a/tests/ui/check-cfg/well-known-values.stderr +++ b/tests/ui/check-cfg/well-known-values.stderr @@ -129,7 +129,7 @@ warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` LL | target_abi = "_UNEXPECTED_VALUE", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` + = note: expected values for `target_abi` are: ``, `abi64`, `abiv2`, `abiv2hf`, `eabi`, `eabihf`, `elfv1`, `elfv2`, `fortanix`, `ilp32`, `ilp32e`, `llvm`, `macabi`, `pauthtest`, `sim`, `softfloat`, `spe`, `uwp`, `vec-extabi`, and `x32` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition value: `_UNEXPECTED_VALUE` 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/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs new file mode 100644 index 0000000000000..6838e749fd333 --- /dev/null +++ b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.rs @@ -0,0 +1,12 @@ +//@ ignore-backends: gcc +//@ check-fail +//@ needs-llvm-components: aarch64 + +//@ compile-flags: -Zpointer-authentication=+function-pointer-type-discrimination --crate-type=lib --target aarch64-unknown-linux-pauthtest + +#![feature(no_core)] +#![no_std] +#![no_main] +#![no_core] + +//~? ERROR function pointer type discrimination is not supported diff --git a/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr new file mode 100644 index 0000000000000..c040b0cb61f66 --- /dev/null +++ b/tests/ui/pointer_authentication/type_discrimination_not_supported_pointer_authentication.stderr @@ -0,0 +1,4 @@ +error: function pointer type discrimination is not supported + +error: aborting due to 1 previous error + diff --git a/tests/ui/process/nofile-limit.rs b/tests/ui/process/nofile-limit.rs index f5246856b80f2..96b298cc78105 100644 --- a/tests/ui/process/nofile-limit.rs +++ b/tests/ui/process/nofile-limit.rs @@ -8,6 +8,9 @@ //@ no-prefer-dynamic //@ compile-flags: -Ctarget-feature=+crt-static -Crpath=no -Crelocation-model=static //@ ignore-backends: gcc +// aarch64-unknown-linux-pauthtest requires dynamic linking, which makes use of file descriptors. +// Setting RLIMIT_NOFILE would result in the binary failing even before main is reached. +//@ ignore-pauthtest #![feature(exit_status_error)] #![feature(rustc_private)] diff --git a/tests/ui/statics/crt-static-pauthtest.rs b/tests/ui/statics/crt-static-pauthtest.rs new file mode 100644 index 0000000000000..bc5badc600d88 --- /dev/null +++ b/tests/ui/statics/crt-static-pauthtest.rs @@ -0,0 +1,9 @@ +//@ compile-flags: -C target-feature=+crt-static --target aarch64-unknown-linux-pauthtest +//@ needs-llvm-components: aarch64 +//@ only-pauthtest + + +#![feature(no_core)] +#![no_main] + +//~? ERROR pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static` diff --git a/tests/ui/statics/crt-static-pauthtest.stderr b/tests/ui/statics/crt-static-pauthtest.stderr new file mode 100644 index 0000000000000..00ab7fb8079de --- /dev/null +++ b/tests/ui/statics/crt-static-pauthtest.stderr @@ -0,0 +1,4 @@ +error: pointer authentication requires dynamic linking. Statically linked libc is incompatible, disable it using `-C target-feature=-crt-static` + +error: aborting due to 1 previous error +