From e301a29cfb1cc8dd353a680140e4a1ccca1d2a67 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Wed, 25 Oct 2023 12:46:20 -0700 Subject: [PATCH 1/5] CFI: Fix fn items, closures, and Fn trait objects Fix casting between function items, closures, and Fn trait objects by transforming function items, closures, and Fn trait objects into function pointers for encoding. --- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/sty.rs | 10 + compiler/rustc_symbol_mangling/src/lib.rs | 2 + compiler/rustc_symbol_mangling/src/typeid.rs | 42 +- .../src/typeid/typeid_itanium_cxx_abi.rs | 594 ++++++++++++++---- ...pe-metadata-id-itanium-cxx-abi-closures.rs | 55 ++ ...tanium-cxx-abi-closure-decl-def-vs-call.rs | 17 + ...anium-cxx-abi-fn-trait-decl-def-vs-call.rs | 17 + ...adata-id-itanium-cxx-abi-function-types.rs | 38 +- ...-type-metadata-id-itanium-cxx-abi-paths.rs | 12 +- ...metadata-id-itanium-cxx-abi-trait-types.rs | 13 +- tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs | 14 +- .../ui/sanitizer/cfi-closure-fn-trait-cast.rs | 11 + .../sanitizer/cfi-dynamic-fn-subtrait-call.rs | 16 + 14 files changed, 673 insertions(+), 170 deletions(-) create mode 100644 tests/codegen/sanitizer/cfi/cfi-emit-type-metadata-id-itanium-cxx-abi-closures.rs create mode 100644 tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-closure-decl-def-vs-call.rs create mode 100644 tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-fn-trait-decl-def-vs-call.rs create mode 100644 tests/ui/sanitizer/cfi-closure-fn-trait-cast.rs create mode 100644 tests/ui/sanitizer/cfi-dynamic-fn-subtrait-call.rs diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8a87538e78850..5f92b5ae8e39e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1841,7 +1841,7 @@ impl<'tcx> TyCtxt<'tcx> { /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used /// to identify which traits may define a given associated type to help avoid cycle errors. /// Returns a `DefId` iterator. - fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator + 'tcx { + pub fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator + 'tcx { let mut set = FxHashSet::default(); let mut stack = vec![trait_def_id]; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index c85ee140fa4ec..f5e048027cb0f 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1836,6 +1836,11 @@ impl<'tcx> Ty<'tcx> { self.0.0.flags } + #[inline] + pub fn is_tuple(self) -> bool { + matches!(self.kind(), Tuple(..)) + } + #[inline] pub fn is_unit(self) -> bool { match self.kind() { @@ -2208,6 +2213,11 @@ impl<'tcx> Ty<'tcx> { matches!(self.kind(), FnDef(..) | FnPtr(_)) } + #[inline] + pub fn is_fn_def(self) -> bool { + matches!(self.kind(), FnDef(..)) + } + #[inline] pub fn is_fn_ptr(self) -> bool { matches!(self.kind(), FnPtr(_)) diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 02bb1fde75c1b..1373563f4b133 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -91,6 +91,8 @@ #![doc(rust_logo)] #![feature(rustdoc_internals)] #![allow(internal_features)] +#![feature(iter_order_by)] +#![feature(let_chains)] #[macro_use] extern crate rustc_middle; diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index e8763e49e624b..31efeb44c2c25 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -4,7 +4,7 @@ /// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler, /// see design document in the tracking issue #89653. use bitflags::bitflags; -use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_middle::ty::{Instance, List, Ty, TyCtxt}; use rustc_target::abi::call::FnAbi; use std::hash::Hasher; use twox_hash::XxHash64; @@ -27,7 +27,11 @@ pub fn typeid_for_fnabi<'tcx>( fn_abi: &FnAbi<'tcx, Ty<'tcx>>, options: TypeIdOptions, ) -> String { - typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options) + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, None), + options, + ) } /// Returns a type metadata identifier for the specified Instance. @@ -36,7 +40,16 @@ pub fn typeid_for_instance<'tcx>( instance: &Instance<'tcx>, options: TypeIdOptions, ) -> String { - typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, List::empty()))) + .unwrap_or_else(|instance| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) + }); + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, Some(instance)), + options, + ) } /// Returns a KCFI type metadata identifier for the specified FnAbi. @@ -48,7 +61,14 @@ pub fn kcfi_typeid_for_fnabi<'tcx>( // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, options).as_bytes()); + hash.write( + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, None), + options, + ) + .as_bytes(), + ); hash.finish() as u32 } @@ -58,9 +78,21 @@ pub fn kcfi_typeid_for_instance<'tcx>( instance: &Instance<'tcx>, options: TypeIdOptions, ) -> u32 { + let fn_abi = tcx + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, List::empty()))) + .unwrap_or_else(|instance| { + bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) + }); // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.write( + typeid_itanium_cxx_abi::typeid_for_fnabi( + tcx, + &typeid_itanium_cxx_abi::transform_fnabi(tcx, &fn_abi, options, Some(instance)), + options, + ) + .as_bytes(), + ); hash.finish() as u32 } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 07a382d161d74..9d5bbd12e0cf2 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -277,7 +277,9 @@ fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap, s.push('E'); compress(dict, DictKey::Region(region), &mut s); } - // FIXME(@lcnr): Why is `ReEarlyParam` reachable here. + // This is possible becasuse trait objects created by new_dynamic_trait and + // new_dynamic_fn_trait are created with tcx.lifetimes.re_erased, and non Fn traits are + // transformed into their identities in transform_predicate. RegionKind::ReEarlyParam(..) | RegionKind::ReErased => { s.push_str("u6region"); compress(dict, DictKey::Region(region), &mut s); @@ -638,19 +640,6 @@ fn encode_ty<'tcx>( } // Function types - ty::FnDef(def_id, args) - | ty::Closure(def_id, args) - | ty::CoroutineClosure(def_id, args) => { - // u[IE], where is , - // as vendor extended type. - let mut s = String::new(); - let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); - s.push_str(&encode_args(tcx, args, dict, options)); - compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); - typeid.push_str(&s); - } - ty::Coroutine(def_id, args, ..) => { // u[IE], where is , // as vendor extended type. @@ -720,6 +709,20 @@ fn encode_ty<'tcx>( typeid.push_str(&s); } + // Associated type projections + ty::Alias(_, alias_ty) => { + // u[IE], where is , + // as vendor extended type. + let mut s = String::new(); + let name = encode_ty_name(tcx, alias_ty.def_id); + let _ = write!(s, "u{}{}", name.len(), &name); + s.push_str(&encode_args(tcx, alias_ty.args, dict, options)); + compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); + typeid.push_str(&s); + } + + // This is possible because non Fn traits are transformed into their identities in + // transform_predicate. // Type parameters ty::Param(..) => { // u5param as vendor extended type @@ -729,9 +732,11 @@ fn encode_ty<'tcx>( } // Unexpected types - ty::Alias(..) - | ty::Bound(..) + ty::Bound(..) + | ty::Closure(..) | ty::Error(..) + | ty::FnDef(..) + | ty::CoroutineClosure(..) | ty::CoroutineWitness(..) | ty::Infer(..) | ty::Placeholder(..) => { @@ -742,24 +747,391 @@ fn encode_ty<'tcx>( typeid } -/// Transforms predicates for being encoded and used in the substitution dictionary. -fn transform_predicates<'tcx>( +/// Returns the list of arguments if principal is an Fn trait or Fn subtrait. +#[inline] +fn fn_trait_args<'tcx>( tcx: TyCtxt<'tcx>, predicates: &List>, -) -> &'tcx List> { - let predicates: Vec> = predicates - .iter() - .filter_map(|predicate| match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(trait_ref) => { +) -> Option<&'tcx List>> { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + if let Some(args) = principal_args(predicates) + && !args.is_empty() + && args[0].expect_ty().is_tuple() + { + return Some(args[0].expect_ty().tuple_fields()); + } + if is_fn_subtrait(tcx, predicates) { + return Some(List::empty()); + } else { + bug!("fn_trait_args: unexpected non-tuple arg `{:?}`", principal_args(predicates)); + } + } else { + None + } +} + +/// Returns the output if principal is an Fn trait or Fn subtrait. +#[inline] +fn fn_trait_output<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> Option> { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + if let Some(projection) = predicates.projection_bounds().next() + && let Some(ty) = projection.skip_binder().term.ty() + { + return Some(ty); + } + if is_fn_subtrait(tcx, predicates) { + return Some(Ty::new_unit(tcx)); + } else { + bug!( + "fn_trait_output: unexpected non-term projection `{:?}`", + predicates.projection_bounds().next() + ); + } + } else { + None + } +} + +/// Returns the signature if principal is an Fn trait or Fn subtrait. +#[inline] +fn fn_trait_sig<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> Option> { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + Some(tcx.mk_fn_sig( + fn_trait_args(tcx, predicates).unwrap(), + fn_trait_output(tcx, predicates).unwrap(), + false, + hir::Unsafety::Normal, + Abi::Rust, + )) + } else { + None + } +} + +/// Returns true if the FnAbi is an unpacked "rust-call" FnAbi. +#[inline] +fn is_closure_call_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> bool { + if fn_abi.conv == Conv::Rust && !fn_abi.c_variadic && !fn_abi.args.is_empty() { + match fn_abi.args[0].layout.ty.kind() { + ty::RawPtr(ty, ..) | ty::Ref(_, ty, _) => { + if let ty::Closure(_, args) = ty.kind() { + let fn_sig = + tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + return fn_sig + .skip_binder() + .inputs() + .iter() + .eq_by(&fn_abi.args[1..], |ty, arg| *ty == arg.layout.ty) + && fn_sig.output().skip_binder() == fn_abi.ret.layout.ty; + } + } + _ => {} + } + } + false +} + +/// Returns true if the FnAbi is a dynamic Fn trait FnAbi. +#[inline] +fn is_dynamic_fn_call_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> bool { + if fn_abi.conv == Conv::Rust + && !fn_abi.c_variadic + && !fn_abi.args.is_empty() + && let ty::Ref(_, ty0, _) = fn_abi.args[0].layout.ty.kind() + && let ty::Dynamic(predicates, ..) = ty0.kind() + { + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + return fn_trait_args(tcx, predicates) + .unwrap() + .iter() + .eq_by(&fn_abi.args[1..], |ty, arg| ty == arg.layout.ty) + && fn_trait_output(tcx, predicates).unwrap() == fn_abi.ret.layout.ty; + } + } + false +} + +/// Returns true if the trait object is an Fn subtrait object. +#[inline] +fn is_dynamic_fn_subtrait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Dynamic(predicates, ..) => is_fn_subtrait(tcx, predicates), + _ => false, + } +} + +/// Returns true if the trait object is an Fn trait object. +#[inline] +fn is_dynamic_fn_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + ty::Dynamic(predicates, ..) => is_fn_trait(tcx, predicates), + _ => false, + } +} + +/// Returns true if principal is an Fn subtrait. +#[inline] +fn is_fn_subtrait<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> bool { + if let Some(principal) = predicates.principal() { + for def_id in tcx.super_traits_of(principal.skip_binder().def_id) { + if tcx.is_fn_trait(def_id) { + return true; + } + } + } + false +} + +/// Returns true if principal is an Fn trait. +#[inline] +fn is_fn_trait<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, +) -> bool { + if let Some(principal) = predicates.principal() + && tcx.is_fn_trait(principal.skip_binder().def_id) + { + true + } else { + false + } +} + +/// Creates a trait object. +#[inline] +fn new_dynamic_trait<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + args: GenericArgsRef<'tcx>, +) -> Ty<'tcx> { + let predicate = + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id: def_id, args: args }); + let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); + Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn) +} + +/// Returns the principal list of arguments. +#[inline] +fn principal_args<'tcx>( + predicates: &List>, +) -> Option<&'tcx List>> { + predicates.principal().map(|trait_ref| trait_ref.skip_binder().args) +} + +/// Transforms a closure FnAbi for being encoded and used in the substitution dictionary. +fn transform_closure_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + _options: EncodeTyOptions, +) -> FnAbi<'tcx, Ty<'tcx>> { + if !fn_abi.args.is_empty() + && let ty::Closure(_, args) = fn_abi.args[0].layout.ty.kind() + { + let fn_sig = tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + return tcx + .fn_abi_of_fn_ptr(ty::ParamEnv::empty().and((fn_sig, List::empty()))) + .unwrap_or_else(|fn_abi_error| { + bug!("transform_fnabi: couldn't get fn_abi of fn_sig {:?}", fn_abi_error) + }) + .clone(); + } else if !fn_abi.args.is_empty() { + match fn_abi.args[0].layout.ty.kind() { + ty::RawPtr(ty, ..) | ty::Ref(_, ty, _) => { + if let ty::Closure(_, args) = ty.kind() { + let fn_sig = + tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + return tcx + .fn_abi_of_fn_ptr(ty::ParamEnv::empty().and((fn_sig, List::empty()))) + .unwrap_or_else(|fn_abi_error| { + bug!( + "transform_fnabi: couldn't get fn_abi of fn_sig {:?}", + fn_abi_error + ) + }) + .clone(); + } + } + _ => {} + } + } + + return fn_abi.clone(); +} + +/// Transforms a Fn trait FnAbi for being encoded and used in the substitution dictionary. +fn transform_fn_trait_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + _options: EncodeTyOptions, +) -> FnAbi<'tcx, Ty<'tcx>> { + if !fn_abi.args.is_empty() { + let fn_sig = ty::Binder::bind_with_vars( + tcx.mk_fn_sig( + fn_abi.args[1..].iter().map(|arg| arg.layout.ty), + fn_abi.ret.layout.ty, + false, + hir::Unsafety::Normal, + Abi::Rust, + ), + List::empty(), + ); + return tcx + .fn_abi_of_fn_ptr(ty::ParamEnv::empty().and((fn_sig, List::empty()))) + .unwrap_or_else(|fn_abi_error| { + bug!("transform_fnabi: couldn't get fn_abi of fn_sig {:?}", fn_abi_error) + }) + .clone(); + } + return fn_abi.clone(); +} + +/// Transforms an FnAbi for being encoded and used in the substitution dictionary. +/// +/// typeid_for_fnabi is called at two locations, initially when declaring/defining functions and +/// methods, which it has a lot more context/information (i.e., FnAbi and Instance), and later +/// during code generation at call sites, which it has a lot less contex/information (i.e., an FnAbi +/// only, and after type erasure). +/// +/// In the first call (i.e., when declaring/defining functions and methods), it encodes a given +/// FnAbi and Instance and assigns one or more type ids to a function or method (these type ids are +/// used later used by the LowerTypeTests LLVM pass to aggregate functions/function pointers in +/// groups derived by these type ids). +/// +/// In the second call (i.e., during code generation), it encodes a given FnAbi (i.e., without an +/// Instance, and after type erasure), and uses this type id to test if the given function/function +/// pointer is member of the group derived from this type id. It means that in the first call +/// typeid_for_fnabi (where it assign type ids), it can only include at most as much information +/// that would be available during code generation at call sites, otherwise the type ids will not +/// match. +/// +/// For this, it: +/// +/// * Replaces the concrete self of trait default implementations and trait implementations by a +/// reference to a trait object (i.e., performs type erasure), similarly to what the compiler does +/// during code generation at call sites, but uses the indentity of the trait that implements the +/// method being declared/defined or called. +/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the entry in +/// the vtable (i.e., the signature of the closure passed as an argument to the shim). +/// * Adjusts the type ids of ClosureOnceShims to the type id expected in the call sites for that +/// entry in the vtable (i.e., the signature of the closure passed as an argument to the shim). +/// +/// For compatibility between types, it uses their least common denominator and: +/// +/// * Transforms closure FnAbis into funtion pointer FnAbis. +/// * Transforms Fn trait and Fn subtrait FnAbis into function pointer FnAbis. +/// +pub fn transform_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: EncodeTyOptions, + instance: Option<&Instance<'tcx>>, +) -> FnAbi<'tcx, Ty<'tcx>> { + if let Some(instance) = instance { + match instance.def { + ty::InstanceDef::Item(..) => { + if let Some(impl_def_id) = tcx.impl_of_method(instance.def_id()) + && let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) + && !fn_abi.args.is_empty() + && fn_abi.args[0].layout.ty.is_ref() + { + // Replace the concrete self by a reference to a trait object (i.e., perform + // type erasure). + let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { + Ty::new_mut_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + trait_ref.skip_binder().def_id, + trait_ref.skip_binder().args, + ), + ) + } else { + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + trait_ref.skip_binder().def_id, + trait_ref.skip_binder().args, + ), + ) + }; + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here + // because the other fields are never used. + fn_abi.args[0].layout.ty = self_ty; + return fn_abi; + } else if tcx.is_closure_like(instance.def_id()) { + // Transform closure declaration/definition FnAbis into function pointer FnAbis + return transform_closure_fnabi(tcx, fn_abi, options); + } + } + ty::InstanceDef::VTableShim(..) | ty::InstanceDef::ClosureOnceShim { .. } => { + // Adjust the type ids of VTableShims and ClosureOnceShims + return transform_closure_fnabi(tcx, fn_abi, options); + } + _ => {} + } + } else { + if is_closure_call_fnabi(tcx, fn_abi) { + // Transform closure call FnAbis into function pointer FnAbis + return transform_closure_fnabi(tcx, fn_abi, options); + } else if is_dynamic_fn_call_fnabi(tcx, fn_abi) { + // Transform Fn trait call FnAbis into function pointer FnAbis + return transform_fn_trait_fnabi(tcx, fn_abi, options); + } + } + return fn_abi.clone(); +} + +/// Transforms a predicate for being encoded and used in the substitution dictionary. +fn transform_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + predicate: ty::PolyExistentialPredicate<'tcx>, + _options: EncodeTyOptions, +) -> Option> { + match predicate.as_ref().skip_binder() { + ty::ExistentialPredicate::Trait(trait_ref) => { + if tcx.is_fn_trait(trait_ref.def_id) { + // Fn trait objects are transformed into function pointers in transform_ty + bug!("transform_predicate: unexpected Fn trait `{}`", trait_ref) + } else { + // Transform non Fn trait objects into their identities let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id); Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait( ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), ))) } - ty::ExistentialPredicate::Projection(..) => None, - ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), - }) + } + // Remove projections + ty::ExistentialPredicate::Projection(..) => None, + ty::ExistentialPredicate::AutoTrait(..) => Some(predicate), + } +} + +/// Transforms predicates for being encoded and used in the substitution dictionary. +fn transform_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: &List>, + options: EncodeTyOptions, +) -> &'tcx List> { + let mut predicates: Vec> = predicates + .iter() + .filter_map(|predicate| transform_predicate(tcx, predicate, options)) .collect(); + + // Enforce predicates ordering when encoding + predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); tcx.mk_poly_existential_predicates(&predicates) } @@ -777,15 +1149,31 @@ fn transform_args<'tcx>( tcx.mk_args_from_iter(args) } -// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all -// c_void types into unit types unconditionally, generalizes pointers if -// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if -// TransformTyOptions::NORMALIZE_INTEGERS option is set. +/// Transforms a ty:Ty for being encoded and used in the substitution dictionary. +/// +/// * Transforms all c_void types into unit types. +/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set. +/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set. +/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and +/// either references itself or any other type that contains or references itself, to avoid a +/// reference cycle. +/// * Transforms repr(transparent) types without non-ZST field into (). +/// * Transforms function items into function pointers. +/// * Transforms closures into function pointers. +/// * Transforms Fn trait objects into function pointers. +/// * Transforms non Fn trait objects into their identities (in transform_predicate). +/// fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> { let mut ty = ty; match ty.kind() { - ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {} + ty::Alias(..) + | ty::Coroutine(..) + | ty::Float(..) + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::Param(..) => {} ty::Bool => { if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { @@ -899,34 +1287,43 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } } - ty::FnDef(def_id, args) => { - ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, options)); + ty::FnDef(def_id, ..) => { + // Transform function items into function pointers + let fn_sig = tcx.fn_sig(def_id).skip_binder(); + let fn_ptr = Ty::new_fn_ptr(tcx, fn_sig); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); } - ty::Closure(def_id, args) => { - ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, options)); - } - - ty::CoroutineClosure(def_id, args) => { - ty = Ty::new_coroutine_closure(tcx, *def_id, transform_args(tcx, args, options)); - } - - ty::Coroutine(def_id, args) => { - ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, options)); + ty::Closure(_, args) => { + // Transform closures into function pointers + let fn_sig = tcx.signature_unclosure(args.as_closure().sig(), hir::Unsafety::Normal); + let fn_ptr = Ty::new_fn_ptr(tcx, fn_sig); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); } ty::Ref(region, ty0, ..) => { - if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } else { - ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); - } + // Remove references from function items, closures, Fn trait and Fn subtrait objects + if ty0.is_fn_def() + || ty0.is_closure() + || is_dynamic_fn_trait(tcx, *ty0) + || is_dynamic_fn_subtrait(tcx, *ty0) + { + ty = transform_ty(tcx, *ty0, options); } else { - if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { + if ty.is_mutable_ptr() { + ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); + } else { + ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); + } } else { - ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + if ty.is_mutable_ptr() { + ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + } else { + ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + } } } } @@ -975,23 +1372,31 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } ty::Dynamic(predicates, _region, kind) => { - ty = Ty::new_dynamic( - tcx, - transform_predicates(tcx, predicates), - tcx.lifetimes.re_erased, - *kind, - ); - } - - ty::Alias(..) => { - ty = transform_ty( - tcx, - tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty), - options, - ); + if is_fn_trait(tcx, predicates) || is_fn_subtrait(tcx, predicates) { + // Transform Fn trait and subtrait objects into function pointers + let fn_sig = ty::Binder::bind_with_vars( + fn_trait_sig(tcx, predicates).unwrap(), + List::empty(), + ); + let fn_ptr = Ty::new_fn_ptr(tcx, fn_sig); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); + } else { + ty = Ty::new_dynamic( + tcx, + transform_predicates(tcx, predicates, options), + tcx.lifetimes.re_erased, + *kind, + ); + } } - ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { + ty::Bound(..) + | ty::Error(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Infer(..) + | ty::Placeholder(..) => { bug!("transform_ty: unexpected `{:?}`", ty.kind()); } } @@ -1083,58 +1488,3 @@ pub fn typeid_for_fnabi<'tcx>( typeid } - -/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with -/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. -pub fn typeid_for_instance<'tcx>( - tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, - options: TypeIdOptions, -) -> String { - let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty()))) - .unwrap_or_else(|instance| { - bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) - }); - - // If this instance is a method and self is a reference, get the impl it belongs to - let impl_def_id = tcx.impl_of_method(instance.def_id()); - if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() { - // If this impl is not an inherent impl, get the trait it implements - if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) { - // Transform the concrete self into a reference to a trait object - let existential_predicate = trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy( - existential_predicate.skip_binder(), - )]); - // Is the concrete self mutable? - let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { - Ty::new_mut_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - } else { - Ty::new_imm_ref( - tcx, - tcx.lifetimes.re_erased, - Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn), - ) - }; - - // Replace the concrete self in an fn_abi clone by the reference to a trait object - let mut fn_abi = fn_abi.clone(); - // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the - // other fields are never used. - fn_abi.args[0].layout.ty = self_ty; - - return typeid_for_fnabi(tcx, &fn_abi, options); - } - } - - typeid_for_fnabi(tcx, fn_abi, options) -} diff --git a/tests/codegen/sanitizer/cfi/cfi-emit-type-metadata-id-itanium-cxx-abi-closures.rs b/tests/codegen/sanitizer/cfi/cfi-emit-type-metadata-id-itanium-cxx-abi-closures.rs new file mode 100644 index 0000000000000..56d99b166a0de --- /dev/null +++ b/tests/codegen/sanitizer/cfi/cfi-emit-type-metadata-id-itanium-cxx-abi-closures.rs @@ -0,0 +1,55 @@ +// Verifies that type metadata identifiers for closures are emitted correctly. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 + +#![crate_type="lib"] +pub fn foo1(a: fn(i32) -> i32) { + // CHECK-LABEL: define{{.*}}4foo1{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"[[TYPE1B:_ZTSFu3i32S_E]]") + a(1); +} + +pub fn bar1() { + foo1(|a| -> i32 { a + 1 }); + // CHECK-LABEL: define{{.*}}4bar1{{.*}}$u7b$$u7b$closure$u7d$$u7d + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + fn a(a: i32) -> i32 { a + 1 } + foo1(a); + // CHECK-LABEL: define{{.*}}4bar11a + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +} + +pub fn foo2(a: &dyn Fn(i32) -> i32) { + // CHECK-LABEL: define{{.*}}4foo2{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"[[TYPE1B]]") + a(1); +} + +pub fn bar2() { + foo2(&|a: i32| -> i32 { a + 1 }); + // CHECK-LABEL: define{{.*}}4bar2{{.*}}$u7b$$u7b$closure$u7d$$u7d + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + fn a(a: i32) -> i32 { a + 1 } + foo2(&a); + // CHECK-LABEL: define{{.*}}4bar21a + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +} + +pub fn foo3(a: &mut dyn FnMut(i32) -> i32) { + // CHECK-LABEL: define{{.*}}4foo3{{.*}}!type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"[[TYPE1B]]") + a(1); +} + +pub fn bar3() { + foo3(&mut |a: i32| -> i32 { a + 1 }); + // CHECK-LABEL: define{{.*}}4bar3{{.*}}$u7b$$u7b$closure$u7d$$u7d + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + fn a(a: i32) -> i32 { a + 1 } + foo3(&mut a); + // CHECK-LABEL: define{{.*}}4bar31a + // CHECK-SAME: {{.*}}!type ![[TYPE1A:[[:print:]]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +} + +// CHECK: ![[TYPE1A]] = !{i64 0, !"[[TYPE1B]]"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-closure-decl-def-vs-call.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-closure-decl-def-vs-call.rs new file mode 100644 index 0000000000000..24a985e847503 --- /dev/null +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-closure-decl-def-vs-call.rs @@ -0,0 +1,17 @@ +// Verifies that type metadata identifiers for closure declaration/definition FnAbis are +// disambiguated from closure call FnAbis. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static + +#![crate_type="lib"] + +pub fn foo(f: fn()) { + // CHECK-LABEL: define{{.*}}3foo + // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"_ZTSFvvE") + f(); +} + + +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvvEE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-fn-trait-decl-def-vs-call.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-fn-trait-decl-def-vs-call.rs new file mode 100644 index 0000000000000..0eff39d8e4281 --- /dev/null +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-fn-trait-decl-def-vs-call.rs @@ -0,0 +1,17 @@ +// Verifies that type metadata identifiers for dynamic Fn trait declaration/definition FnAbis are +// disambiguated from dynamic Fn trait call FnAbis. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static + +#![crate_type="lib"] + +pub fn foo(f: &dyn Fn()) { + // CHECK-LABEL: define{{.*}}3foo + // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: call i1 @llvm.type.test(ptr {{%.+}}, metadata !"_ZTSFvvE") + f(); +} + + +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvvEE"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs index ab3d339989b38..240e9959e3c8b 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-function-types.rs @@ -6,6 +6,8 @@ #![crate_type="lib"] +trait FnSubtrait: Fn() {} + pub fn foo1(_: fn(i32) -> i32) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo2(_: fn(i32) -> i32, _: fn(i32) -> i32) { } @@ -13,33 +15,33 @@ pub fn foo2(_: fn(i32) -> i32, _: fn(i32) -> i32) { } pub fn foo3(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) { } // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo4(_: &dyn Fn(i32) -> i32) { } -// CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo5(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { } -// CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE2]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo6(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { } -// CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE3]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo7(_: &dyn FnMut(i32) -> i32) { } -// CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo8(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { } -// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE2]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo9(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { } -// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE3]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo10(_: &dyn FnOnce(i32) -> i32) { } -// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE1]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo11(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) { } -// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE2]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo12(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) {} -// CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE3]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo13(_: &dyn FnSubtrait) { } +// CHECK: define{{.*}}5foo13{{.*}}!type ![[TYPE13:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo14(_: &dyn FnSubtrait, _: &dyn FnSubtrait) { } +// CHECK: define{{.*}}5foo14{{.*}}!type ![[TYPE14:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo15(_: &dyn FnSubtrait, _: &dyn FnSubtrait, _: &dyn FnSubtrait) {} +// CHECK: define{{.*}}5foo15{{.*}}!type ![[TYPE15:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFu3i32S_EE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_E"} // CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_S0_E"} -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEEE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEES3_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5paramEu6regionEES3_S3_E"} -// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEEE"} -// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEES3_E"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5paramEu6regionEES3_S3_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEEE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEES3_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5paramEu6regionEES3_S3_E"} +// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvPFvvEE"} +// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvPFvvES_E"} +// CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvPFvvES_S_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs index ca781a99296be..79f75af4e68cd 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs @@ -74,15 +74,15 @@ pub fn foo11(_: &Type4, _: &Type4) { } pub fn foo12(_: &Type4, _: &Type4, _: &Type4) { } // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barEE"} -// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_E"} -// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo10{{[{}][{}]}}extern{{[}][}]}}3barES0_S0_E"} +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvvEE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvPFvvES_E"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPFvvES_S_E"} // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"} // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"} // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"} // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"} // CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barEE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo8{{[{}][{}]}}impl{{[}][}]}}3barES0_S0_E"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEEE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEES1_E"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEES1_S1_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs index cc7178e41c716..c3ff5062e5c5a 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-trait-types.rs @@ -136,16 +136,15 @@ pub fn foo27(_: &dyn Trait5, _: &dyn Trait5, _: &dyn Trait // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_E"} // CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_S2_E"} -// FIXME(rcvalle): Enforce autotraits ordering when encoding (e.g., alphabetical order) -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEEE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES3_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES3_S3_E"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_E"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_S3_E"} // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_E"} // CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES3_S3_E"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEEE"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES4_E"} -// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker{{(4Send|4Sync)}}u6regionEES4_S4_E"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES4_E"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Syncu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES4_S4_E"} // CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEES2_E"} // CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEES2_S2_E"} // CHECK: ![[TYPE17]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait2Iu5paramEu6regionEES3_E"} diff --git a/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs index a46a3afd7343a..caad3094d5a47 100644 --- a/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs +++ b/tests/ui/sanitizer/cfi-closure-fn-ptr-cast.rs @@ -1,18 +1,10 @@ -// Tests that converting a closure to a function pointer works -// The notable thing being tested here is that when the closure does not capture anything, -// the call method from its Fn trait takes a ZST representing its environment. The compiler then -// uses the assumption that the ZST is non-passed to reify this into a function pointer. +// Verifies that casting a closure to a function pointer works. // -// This checks that the reified function pointer will have the expected alias set at its call-site. - //@ needs-sanitizer-cfi -// FIXME(#122848) Remove only-linux once OSX CFI binaries work -//@ only-linux -//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi -//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0 +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 //@ run-pass -pub fn main() { +fn main() { let f: &fn() = &((|| ()) as _); f(); } diff --git a/tests/ui/sanitizer/cfi-closure-fn-trait-cast.rs b/tests/ui/sanitizer/cfi-closure-fn-trait-cast.rs new file mode 100644 index 0000000000000..f0ac80c773687 --- /dev/null +++ b/tests/ui/sanitizer/cfi-closure-fn-trait-cast.rs @@ -0,0 +1,11 @@ +// Verifies that casting a closure to a Fn trait object works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +#![feature(fn_traits)] +fn main() { + let f: &(dyn Fn()) = &(|| {}) as _; + f.call(()); +} diff --git a/tests/ui/sanitizer/cfi-dynamic-fn-subtrait-call.rs b/tests/ui/sanitizer/cfi-dynamic-fn-subtrait-call.rs new file mode 100644 index 0000000000000..02e54278ceee8 --- /dev/null +++ b/tests/ui/sanitizer/cfi-dynamic-fn-subtrait-call.rs @@ -0,0 +1,16 @@ +// Verifies that calling a dynamic Fn subtrait object works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +trait FnSubtrait: Fn() {} +impl FnSubtrait for T {} + +fn call_dynamic_fn_subtrait(f: &dyn FnSubtrait) { + f(); +} + +fn main() { + call_dynamic_fn_subtrait(&|| {}); +} From 4319fbeb08d5b76e3b1e8de6e9217431f9fddf4c Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Tue, 19 Mar 2024 19:04:35 -0700 Subject: [PATCH 2/5] CFI: Fix drop and drop_in_place Fix drop and drop_in_place by transforming self of drop and drop_in_place methods into a Drop trait object and handling drop method calls separatedly (and also transforming self into a Drop trait object). This has the same effect of what CFI does for impl methods by transforming self into trait objects that match the trait objects in the calls after type erasure. --- compiler/rustc_codegen_gcc/src/asm.rs | 2 +- compiler/rustc_codegen_gcc/src/builder.rs | 7 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 11 +-- compiler/rustc_codegen_llvm/src/asm.rs | 6 +- compiler/rustc_codegen_llvm/src/builder.rs | 38 ++++++--- compiler/rustc_codegen_llvm/src/intrinsic.rs | 39 +++++---- compiler/rustc_codegen_ssa/src/base.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 26 +++++- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 2 +- compiler/rustc_codegen_ssa/src/size_of_val.rs | 10 ++- .../rustc_codegen_ssa/src/traits/builder.rs | 2 + compiler/rustc_symbol_mangling/src/typeid.rs | 22 +++++ .../src/typeid/typeid_itanium_cxx_abi.rs | 82 +++++++++++++++++++ tests/ui/sanitizer/cfi-drop-in-place.rs | 14 ++++ 14 files changed, 218 insertions(+), 45 deletions(-) create mode 100644 tests/ui/sanitizer/cfi-drop-in-place.rs diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 9b679019e96ce..949df2a36fe71 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -541,7 +541,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) }; - self.call(self.type_void(), None, None, builtin_unreachable, &[], None); + self.call(self.type_void(), None, None, builtin_unreachable, &[], None, false); } // Write results to outputs. diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f5cda81f6ab86..7eca0e1bf94ff 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -592,12 +592,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>, + _is_drop: bool ) -> RValue<'gcc> { let try_block = self.current_func().new_block("try"); let current_block = self.block.clone(); self.block = try_block; - let call = self.call(typ, fn_attrs, None, func, args, None); // TODO(antoyo): use funclet here? + let call = self.call(typ, fn_attrs, None, func, args, None, false); // TODO(antoyo): use funclet here? self.block = current_block; let return_value = @@ -629,8 +630,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>, + _is_drop: bool ) -> RValue<'gcc> { - let call_site = self.call(typ, fn_attrs, None, func, args, None); + let call_site = self.call(typ, fn_attrs, None, func, args, None, false); let condition = self.context.new_rvalue_from_int(self.bool_type, 1); self.llbb().end_with_conditional(self.location, condition, then, catch); if let Some(_fn_abi) = fn_abi { @@ -1667,6 +1669,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>, + _is_drop: bool ) -> RValue<'gcc> { // FIXME(antoyo): remove when having a proper API. let gcc_func = unsafe { std::mem::transmute(func) }; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a6c8b72e851b2..bef7d271cda46 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -133,6 +133,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { func, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + false ) } sym::likely => self.expect(args[0].immediate(), true), @@ -401,7 +402,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn abort(&mut self) { let func = self.context.get_builtin_function("abort"); let func: RValue<'gcc> = unsafe { std::mem::transmute(func) }; - self.call(self.type_void(), None, None, func, &[], None); + self.call(self.type_void(), None, None, func, &[], None, false); } fn assume(&mut self, value: Self::Value) { @@ -1103,7 +1104,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( dest: RValue<'gcc>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { - bx.call(bx.type_void(), None, None, try_func, &[data], None); + bx.call(bx.type_void(), None, None, try_func, &[data], None, false); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx.data_layout.i32_align.abi; @@ -1177,21 +1178,21 @@ fn codegen_gnu_try<'gcc>( let zero = bx.cx.context.new_rvalue_zero(bx.int_type); let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]); let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, false); bx.ret(bx.const_i32(1)); // NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not // generate a try/catch. // FIXME(antoyo): add a check in the libgccjit API to prevent this. bx.switch_to_block(current_block); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, false); }); let func = unsafe { std::mem::transmute(func) }; // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 74539d4d49570..681fb37e0855f 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -466,11 +466,11 @@ pub(crate) fn inline_asm_call<'ll>( let call = if !labels.is_empty() { assert!(catch_funclet.is_none()); - bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None) + bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, false) } else if let Some((catch, funclet)) = catch_funclet { - bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet) + bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, false) } else { - bx.call(fty, None, None, v, inputs, None) + bx.call(fty, None, None, v, inputs, None, false) }; // Store mark in a metadata node so we can map LLVM errors diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 63e59ea13fc35..7efa8319e0fae 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -21,7 +21,10 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; -use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions}; +use rustc_symbol_mangling::typeid::{ + kcfi_typeid_for_drop_fnabi, kcfi_typeid_for_fnabi, typeid_for_drop_fnabi, typeid_for_fnabi, + TypeIdOptions, +}; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; @@ -221,6 +224,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { then: &'ll BasicBlock, catch: &'ll BasicBlock, funclet: Option<&Funclet<'ll>>, + is_drop: bool, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -233,10 +237,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, llfn, is_drop); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn, is_drop); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1231,6 +1235,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { llfn: &'ll Value, args: &[&'ll Value], funclet: Option<&Funclet<'ll>>, + is_drop: bool, ) -> &'ll Value { debug!("call {:?} with args ({:?})", llfn, args); @@ -1243,10 +1248,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, llfn, is_drop); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn, is_drop); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1468,7 +1473,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); - self.call(ty, None, None, f, args, None) + self.call(ty, None, None, f, args, None, false) } fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) { @@ -1526,7 +1531,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { format!("llvm.{instr}.sat.i{int_width}.f{float_width}") }; let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); - self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None) + self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None, false) } pub(crate) fn landing_pad( @@ -1554,6 +1559,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { default_dest: &'ll BasicBlock, indirect_dest: &[&'ll BasicBlock], funclet: Option<&Funclet<'ll>>, + is_drop: bool, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -1566,10 +1572,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, llfn, is_drop); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn, is_drop); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1602,6 +1608,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, llfn: &'ll Value, + is_drop: bool, ) { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; if self.tcx.sess.is_sanitizer_cfi_enabled() @@ -1622,7 +1629,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + let typeid = if is_drop { + typeid_for_drop_fnabi(self.tcx, fn_abi, options) + } else { + typeid_for_fnabi(self.tcx, fn_abi, options) + }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); // Test whether the function pointer is associated with the type identifier. @@ -1645,6 +1656,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, llfn: &'ll Value, + is_drop: bool, ) -> Option> { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; let kcfi_bundle = if self.tcx.sess.is_sanitizer_kcfi_enabled() @@ -1665,7 +1677,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = if is_drop { + kcfi_typeid_for_drop_fnabi(self.tcx, fn_abi, options) + } else { + kcfi_typeid_for_fnabi(self.tcx, fn_abi, options) + }; Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)])) } else { None diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 2409b2e78d736..660d78a0b784a 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -181,6 +181,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { simple_fn, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + false, ) } sym::likely => { @@ -539,7 +540,7 @@ fn catch_unwind_intrinsic<'ll>( ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.call(try_func_ty, None, None, try_func, &[data], None); + bx.call(try_func_ty, None, None, try_func, &[data], None, false); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx().data_layout.i32_align.abi; @@ -640,7 +641,7 @@ fn codegen_msvc_try<'ll>( let ptr_align = bx.tcx().data_layout.pointer_align.abi; let slot = bx.alloca(bx.type_ptr(), ptr_align); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, false); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -684,7 +685,7 @@ fn codegen_msvc_try<'ll>( let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]); let ptr = bx.load(bx.type_ptr(), slot, ptr_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), false); bx.catch_ret(&funclet, caught); // The flag value of 64 indicates a "catch-all". @@ -692,7 +693,7 @@ fn codegen_msvc_try<'ll>( let flags = bx.const_i32(64); let null = bx.const_null(bx.type_ptr()); let funclet = bx.catch_pad(cs, &[null, flags, null]); - bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet), false); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -701,7 +702,7 @@ fn codegen_msvc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -750,7 +751,7 @@ fn codegen_wasm_try<'ll>( // } // let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, false); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -766,7 +767,7 @@ fn codegen_wasm_try<'ll>( let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), false); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -775,7 +776,7 @@ fn codegen_wasm_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -818,7 +819,7 @@ fn codegen_gnu_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, false); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -836,13 +837,13 @@ fn codegen_gnu_try<'ll>( bx.add_clause(vals, tydesc); let ptr = bx.extract_value(vals, 0); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, false); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -882,7 +883,7 @@ fn codegen_emcc_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, false); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -920,13 +921,13 @@ fn codegen_emcc_try<'ll>( bx.store(is_rust_panic, catch_data_1, i8_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None); + bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None, false); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, false); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -1439,6 +1440,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &args.iter().map(|arg| arg.immediate()).collect::>(), None, + false, ); Ok(c) } @@ -1607,6 +1609,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None, + false, ); return Ok(v); } @@ -1706,6 +1709,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[2].immediate()], None, + false, ); return Ok(v); } @@ -1799,6 +1803,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[2].immediate(), args[1].immediate(), alignment, mask], None, + false, ); return Ok(v); } @@ -1904,6 +1909,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None, + false, ); return Ok(v); } @@ -2352,11 +2358,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), bx.const_int(bx.type_i1(), 0)], None, + false, )) } else { let fn_ty = bx.type_func(&[vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None)) + Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, false)) }; } @@ -2409,7 +2416,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None); + let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, false); return Ok(v); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 13809ef72ec7b..a4dc8494df100 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -482,7 +482,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (rust_main, start_ty, vec![arg_argc, arg_argv]) }; - let result = bx.call(start_ty, None, None, start_fn, &args, None); + let result = bx.call(start_ty, None, None, start_fn, &args, None, false); if cx.sess().target.os.contains("uefi") { bx.ret(result); } else { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index dcc27a4f0e568..adf6eac17bed2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -160,6 +160,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { mut unwind: mir::UnwindAction, copied_constant_arguments: &[PlaceRef<'tcx, ::Value>], instance: Option>, + is_drop: bool, mergeable_succ: bool, ) -> MergingSucc { let tcx = bx.tcx(); @@ -232,6 +233,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { ret_llbb, unwind_block, self.funclet(fx), + is_drop, ); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(invokeret); @@ -247,7 +249,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { } MergingSucc::False } else { - let llret = bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx)); + let llret = + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx), false); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(llret); } @@ -606,6 +609,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { unwind, &[], Some(instance), + true, mergeable_succ, ) } @@ -685,8 +689,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (fn_abi, llfn, instance) = common::build_langcall(bx, Some(span), lang_item); // Codegen the actual panic invoke/call. - let merging_succ = - helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], Some(instance), false); + let merging_succ = helper.do_call( + self, + bx, + fn_abi, + llfn, + &args, + None, + unwind, + &[], + Some(instance), + false, + false, + ); assert_eq!(merging_succ, MergingSucc::False); MergingSucc::False } @@ -716,6 +731,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &[], Some(instance), false, + false, ); assert_eq!(merging_succ, MergingSucc::False); } @@ -778,6 +794,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { unwind, &[], Some(instance), + false, mergeable_succ, ) } else { @@ -1135,6 +1152,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { unwind, &copied_constant_arguments, instance, + false, mergeable_succ, ) } @@ -1699,7 +1717,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref(), false); bx.apply_attrs_to_cleanup_callsite(llret); } diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 15f2e0e56d86d..779380d01b176 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -710,7 +710,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { None }; - bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None) + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, false) } else { bx.get_static(def_id) }; diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index e2e95cede60a5..890818a478403 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -70,7 +70,15 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // (But we are in good company, this code is duplicated plenty of times.) let fn_ty = bx.fn_decl_backend_type(fn_abi); - bx.call(fn_ty, /* fn_attrs */ None, Some(fn_abi), llfn, &[msg.0, msg.1], None); + bx.call( + fn_ty, + /* fn_attrs */ None, + Some(fn_abi), + llfn, + &[msg.0, msg.1], + None, + false, + ); // This function does not return so we can now return whatever we want. let size = bx.const_usize(layout.size.bytes()); diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 36f37e3791bc5..2f140ea363f02 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -80,6 +80,7 @@ pub trait BuilderMethods<'a, 'tcx>: then: Self::BasicBlock, catch: Self::BasicBlock, funclet: Option<&Self::Funclet>, + is_drop: bool, ) -> Self::Value; fn unreachable(&mut self); @@ -339,6 +340,7 @@ pub trait BuilderMethods<'a, 'tcx>: llfn: Self::Value, args: &[Self::Value], funclet: Option<&Self::Funclet>, + is_drop: bool, ) -> Self::Value; fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index 31efeb44c2c25..ab49403568465 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -21,6 +21,15 @@ bitflags! { mod typeid_itanium_cxx_abi; +/// Returns a type metadata identifier for the specified drop FnAbi. +pub fn typeid_for_drop_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + typeid_itanium_cxx_abi::typeid_for_drop_fnabi(tcx, fn_abi, options) +} + /// Returns a type metadata identifier for the specified FnAbi. pub fn typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, @@ -52,6 +61,19 @@ pub fn typeid_for_instance<'tcx>( ) } +/// Returns a KCFI type metadata identifier for the specified drop FnAbi. +pub fn kcfi_typeid_for_drop_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> u32 { + // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the + // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) + let mut hash: XxHash64 = Default::default(); + hash.write(typeid_itanium_cxx_abi::typeid_for_drop_fnabi(tcx, fn_abi, options).as_bytes()); + hash.finish() as u32 +} + /// Returns a KCFI type metadata identifier for the specified FnAbi. pub fn kcfi_typeid_for_fnabi<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 9d5bbd12e0cf2..2e8b74731f98d 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -1023,6 +1023,8 @@ fn transform_fn_trait_fnabi<'tcx>( /// the vtable (i.e., the signature of the closure passed as an argument to the shim). /// * Adjusts the type ids of ClosureOnceShims to the type id expected in the call sites for that /// entry in the vtable (i.e., the signature of the closure passed as an argument to the shim). +/// * Adjusts the type ids of DropGlues to the type id expected in the call sites for that +/// entry in the vtable (i.e., a call on a synthesized Drop trait object). /// /// For compatibility between types, it uses their least common denominator and: /// @@ -1080,6 +1082,47 @@ pub fn transform_fnabi<'tcx>( // Adjust the type ids of VTableShims and ClosureOnceShims return transform_closure_fnabi(tcx, fn_abi, options); } + ty::InstanceDef::DropGlue(..) => { + // Adjust the type ids of DropGlues + // + // DropGlues may have indirect calls to one or more given types drop function. + // However, Rust allows for types to be erased to any trait object and retains the + // drop function for the original type, which means at the indirect call sites in + // DropGlues, when typeid_for_fnabi is called a second time, it only has an FnAbi + // (i.e., without an Instance, and after type erasure), and it could be an FnAbi to + // a call on any arbitrary trait object, so normalize them to a synthesized Drop + // trait object, both on declaration/definition, and during code generation at call + // sites so they have the same type id and match. + if !fn_abi.args.is_empty() { + let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { + Ty::new_mut_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + tcx.lang_items().drop_trait().unwrap(), + List::empty(), + ), + ) + } else { + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + tcx.lang_items().drop_trait().unwrap(), + List::empty(), + ), + ) + }; + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here + // because the other fields are never used. + fn_abi.args[0].layout.ty = self_ty; + return fn_abi; + } + return fn_abi.clone(); + } _ => {} } } else { @@ -1404,6 +1447,45 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ty } +/// Returns a type metadata identifier for the specified drop FnAbi using the Itanium C++ ABI with +/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. +pub fn typeid_for_drop_fnabi<'tcx>( + tcx: TyCtxt<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + options: TypeIdOptions, +) -> String { + // Transform self into a Drop trait object to match what the drop method's self is transformed + // into in transform_fnabi. + // + // DropGlues may have indirect calls to one or more given types drop function. However, Rust + // allows for types to be erased to any trait object and retains the drop function for the + // original type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is + // called a second time, it only has an FnAbi (i.e., without an Instance, and after type + // erasure), and it could be an FnAbi to a call on any arbitrary trait object, so normalize them + // to a synthesized Drop trait object, both on declaration/definition, and during code + // generation at call sites so they have the same type id and match. + let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { + Ty::new_mut_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait(tcx, tcx.lang_items().drop_trait().unwrap(), List::empty()), + ) + } else { + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait(tcx, tcx.lang_items().drop_trait().unwrap(), List::empty()), + ) + }; + + let mut fn_abi = fn_abi.clone(); + // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the other + // fields are never used. + fn_abi.args[0].layout.ty = self_ty; + + return typeid_for_fnabi(tcx, &fn_abi, options); +} + /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor /// extended type qualifiers and types for Rust types that are not used at the FFI boundary. #[instrument(level = "trace", skip(tcx))] diff --git a/tests/ui/sanitizer/cfi-drop-in-place.rs b/tests/ui/sanitizer/cfi-drop-in-place.rs new file mode 100644 index 0000000000000..3c8428cfaeb35 --- /dev/null +++ b/tests/ui/sanitizer/cfi-drop-in-place.rs @@ -0,0 +1,14 @@ +// Verifies that custom drops can be called on arbitraty trait objects. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +struct CustomDrop; +impl Drop for CustomDrop { + fn drop(&mut self) {} +} + +fn main() { + let _ = Box::new(CustomDrop) as Box; +} From 56cf23c46c026ca9f02039d802289911ebdae9a4 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Thu, 21 Mar 2024 13:29:24 -0700 Subject: [PATCH 3/5] CFI: Fix encode_ty: unexpected 'CoroutineWitness' Fix #122705 by adding support for encoding `ty:CoroutineClosure`. --- .../src/typeid/typeid_itanium_cxx_abi.rs | 8 +++++++- .../sanitizer/cfi-async-closure-issue-122705.rs | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/ui/sanitizer/cfi-async-closure-issue-122705.rs diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 2e8b74731f98d..79f697a2b78ea 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -1346,6 +1346,13 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ty = transform_ty(tcx, fn_ptr, options); } + ty::CoroutineClosure(_, args) => { + // Transform async closures into function pointers + let fn_ptr = args.as_coroutine_closure().signature_parts_ty(); + // Transform fn_sig inputs and output + ty = transform_ty(tcx, fn_ptr, options); + } + ty::Ref(region, ty0, ..) => { // Remove references from function items, closures, Fn trait and Fn subtrait objects if ty0.is_fn_def() @@ -1436,7 +1443,6 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ty::Bound(..) | ty::Error(..) - | ty::CoroutineClosure(..) | ty::CoroutineWitness(..) | ty::Infer(..) | ty::Placeholder(..) => { diff --git a/tests/ui/sanitizer/cfi-async-closure-issue-122705.rs b/tests/ui/sanitizer/cfi-async-closure-issue-122705.rs new file mode 100644 index 0000000000000..2f86d2e9fe61c --- /dev/null +++ b/tests/ui/sanitizer/cfi-async-closure-issue-122705.rs @@ -0,0 +1,15 @@ +// Verifies that using async closure works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 --edition=2021 +//@ run-pass + +#![feature(async_closure)] + +#[inline(never)] +fn foo(_: T) {} + +fn main() { + let a = async move |_: i32, _: i32| {}; + foo(a); +} From cbcb16d82a795a5aa1e72422a0141c3e1df9fa25 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Fri, 22 Mar 2024 14:03:23 -0700 Subject: [PATCH 4/5] Revert "CFI: Skip non-passed arguments" This reverts commit f2f0d255df8d8345a481b949b63a35bcc5db7f2f. --- .../src/typeid/typeid_itanium_cxx_abi.rs | 22 ++++------- ...-type-metadata-id-itanium-cxx-abi-paths.rs | 38 +++++++++---------- ...data-id-itanium-cxx-abi-primitive-types.rs | 6 ++- ...-itanium-cxx-abi-repr-transparent-types.rs | 2 +- .../sanitizer/cfi/normalize-integers.rs | 6 +-- 5 files changed, 33 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 79f697a2b78ea..a7f4ca4446343 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{ use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_span::def_id::DefId; use rustc_span::sym; -use rustc_target::abi::call::{Conv, FnAbi, PassMode}; +use rustc_target::abi::call::{Conv, FnAbi}; use rustc_target::abi::Integer; use rustc_target::spec::abi::Abi; use std::fmt::Write as _; @@ -1534,27 +1534,19 @@ pub fn typeid_for_fnabi<'tcx>( typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); // Encode the parameter types - - // We erase ZSTs as we go if the argument is skipped. This is an implementation detail of how - // MIR is currently treated by rustc, and subject to change in the future. Specifically, MIR - // interpretation today will allow skipped arguments to simply not be passed at a call-site. if !fn_abi.c_variadic { - let mut pushed_arg = false; - for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { - pushed_arg = true; - let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); - typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); - } - if !pushed_arg { + if !fn_abi.args.is_empty() { + for arg in fn_abi.args.iter() { + let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); + typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); + } + } else { // Empty parameter lists, whether declared as () or conventionally as (void), are // encoded with a void parameter specifier "v". typeid.push('v'); } } else { for n in 0..fn_abi.fixed_count as usize { - if fn_abi.args[n].mode == PassMode::Ignore { - continue; - } let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs index 79f75af4e68cd..7807aaa6cdb95 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-paths.rs @@ -47,42 +47,40 @@ pub fn foo() where let _: Type4 = ::bar; } -// Force arguments to be passed by using a reference. Otherwise, they may end up PassMode::Ignore - -pub fn foo1(_: &Type1) { } +pub fn foo1(_: Type1) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo2(_: &Type1, _: &Type1) { } +pub fn foo2(_: Type1, _: Type1) { } // CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo3(_: &Type1, _: &Type1, _: &Type1) { } +pub fn foo3(_: Type1, _: Type1, _: Type1) { } // CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo4(_: &Type2) { } +pub fn foo4(_: Type2) { } // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo5(_: &Type2, _: &Type2) { } +pub fn foo5(_: Type2, _: Type2) { } // CHECK: define{{.*}}4foo5{{.*}}!type ![[TYPE5:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo6(_: &Type2, _: &Type2, _: &Type2) { } +pub fn foo6(_: Type2, _: Type2, _: Type2) { } // CHECK: define{{.*}}4foo6{{.*}}!type ![[TYPE6:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo7(_: &Type3) { } +pub fn foo7(_: Type3) { } // CHECK: define{{.*}}4foo7{{.*}}!type ![[TYPE7:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo8(_: &Type3, _: &Type3) { } +pub fn foo8(_: Type3, _: Type3) { } // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo9(_: &Type3, _: &Type3, _: &Type3) { } +pub fn foo9(_: Type3, _: Type3, _: Type3) { } // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo10(_: &Type4) { } +pub fn foo10(_: Type4) { } // CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo11(_: &Type4, _: &Type4) { } +pub fn foo11(_: Type4, _: Type4) { } // CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} -pub fn foo12(_: &Type4, _: &Type4, _: &Type4) { } +pub fn foo12(_: Type4, _: Type4, _: Type4) { } // CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvPFvvEE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvPFvvES_E"} // CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPFvvES_S_E"} -// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooEE"} -// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_E"} -// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooES0_S0_E"} -// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooEE"} -// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_E"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooES0_S0_E"} +// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooE"} +// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooS_E"} +// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo11{{[{}][{}]}}closure{{[}][}]}}3FooS_S_E"} +// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooE"} +// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooS_E"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo12{{[{}][{}]}}constant{{[}][}]}}3FooS_S_E"} // CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEEE"} // CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEES1_E"} // CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvPFvu3refIu{{[0-9]+}}NtNvC{{[[:print:]]+}}_{{[[:print:]]+}}3foo3FooEES1_S1_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs index 38f507856bdee..3a1a09150eae9 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-primitive-types.rs @@ -12,9 +12,9 @@ use core::ffi::*; pub fn foo1(_: ()) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo2(_: (), _: c_void) { } -// CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo2{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo3(_: (), _: c_void, _: c_void) { } -// CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +// CHECK: define{{.*}}4foo3{{.*}}!type ![[TYPE3:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo4(_: *mut ()) { } // CHECK: define{{.*}}4foo4{{.*}}!type ![[TYPE4:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo5(_: *mut (), _: *mut c_void) { } @@ -131,6 +131,8 @@ pub fn foo60(_: &str, _: &str, _: &str) { } // CHECK: define{{.*}}5foo60{{.*}}!type ![[TYPE60:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvvvE"} +// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvvvvE"} // CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvPvE"} // CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvPvS_E"} // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvPvS_S_E"} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs index 6f47f5e335577..0deda029c4b09 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs @@ -28,7 +28,7 @@ pub struct Type2<'a> { member3: &'a Type2<'a>, } -pub struct Bar(i32); +pub struct Bar; // repr(transparent) user-defined generic type #[repr(transparent)] diff --git a/tests/codegen/sanitizer/cfi/normalize-integers.rs b/tests/codegen/sanitizer/cfi/normalize-integers.rs index 801ed312be5b1..210814eb9ae1f 100644 --- a/tests/codegen/sanitizer/cfi/normalize-integers.rs +++ b/tests/codegen/sanitizer/cfi/normalize-integers.rs @@ -41,6 +41,6 @@ pub fn foo11(_: (), _: usize, _: usize, _: usize) { } // CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}E.normalized"} // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}S_E.normalized"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFv{{u3i16|u3i32|u3i64|u4i128}}S_S_E.normalized"} -// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}E.normalized"} -// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}S_E.normalized"} -// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFv{{u3u16|u3u32|u3u64|u4u128}}S_S_E.normalized"} +// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}E.normalized"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}S_E.normalized"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvv{{u3u16|u3u32|u3u64|u4u128}}S_S_E.normalized"} From 66b2708bc41d6a7726ccb9ed4eade4a036842428 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Fri, 22 Mar 2024 15:45:23 -0700 Subject: [PATCH 5/5] CFI: Fix methods as function pointer cast Fix casting between methods and function pointers by assigning a secondary type id to methods with their concrete self so they can be used as function pointers. --- compiler/rustc_codegen_llvm/src/declare.rs | 10 ++++ compiler/rustc_symbol_mangling/src/typeid.rs | 1 + .../src/typeid/typeid_itanium_cxx_abi.rs | 46 +++++++++++-------- ...itanium-cxx-abi-method-secondary-typeid.rs | 22 +++++++++ tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs | 21 +++++++++ 5 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs create mode 100644 tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 78c0725a63784..9d9ddddf2fadb 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -141,20 +141,30 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { + let mut typeids = Vec::new(); let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty()); + typeids.push(typeid.clone()); self.set_type_metadata(llfn, typeid); let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS); + typeids.push(typeid.clone()); self.add_type_metadata(llfn, typeid); let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS); + typeids.push(typeid.clone()); self.add_type_metadata(llfn, typeid); let typeid = typeid_for_instance( self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, ); + typeids.push(typeid.clone()); self.add_type_metadata(llfn, typeid); + let typeid = + typeid_for_instance(self.tcx, &instance, TypeIdOptions::NO_TYPE_ERASURE); + if !typeids.contains(&typeid) { + self.add_type_metadata(llfn, typeid); + } } else { let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); self.set_type_metadata(llfn, typeid); diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index ab49403568465..a0132957946ba 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -16,6 +16,7 @@ bitflags! { const GENERALIZE_POINTERS = 1; const GENERALIZE_REPR_C = 2; const NORMALIZE_INTEGERS = 4; + const NO_TYPE_ERASURE = 8; } } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index a7f4ca4446343..eef1717bfa76c 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -1045,28 +1045,34 @@ pub fn transform_fnabi<'tcx>( && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() { - // Replace the concrete self by a reference to a trait object (i.e., perform - // type erasure). - let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() { - Ty::new_mut_ref( - tcx, - tcx.lifetimes.re_erased, - new_dynamic_trait( - tcx, - trait_ref.skip_binder().def_id, - trait_ref.skip_binder().args, - ), - ) + let self_ty = if options.contains(EncodeTyOptions::NO_TYPE_ERASURE) { + // Do not perform type erasure for assigning a secondary type id to methods + // with their concrete self so they can be used as function pointers. + return fn_abi.clone(); } else { - Ty::new_imm_ref( - tcx, - tcx.lifetimes.re_erased, - new_dynamic_trait( + // Replace the concrete self by a reference to a trait object (i.e., perform + // type erasure). + if fn_abi.args[0].layout.ty.is_mutable_ptr() { + Ty::new_mut_ref( tcx, - trait_ref.skip_binder().def_id, - trait_ref.skip_binder().args, - ), - ) + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + trait_ref.skip_binder().def_id, + trait_ref.skip_binder().args, + ), + ) + } else { + Ty::new_imm_ref( + tcx, + tcx.lifetimes.re_erased, + new_dynamic_trait( + tcx, + trait_ref.skip_binder().def_id, + trait_ref.skip_binder().args, + ), + ) + } }; let mut fn_abi = fn_abi.clone(); // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs new file mode 100644 index 0000000000000..4555c2bfe9ecd --- /dev/null +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-method-secondary-typeid.rs @@ -0,0 +1,22 @@ +// Verifies that a secondary type metadata identifier is assigned to methods with their concrete +// self so they can be used as function pointers. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static + +#![crate_type="lib"] + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) {} + // CHECK: define{{.*}}3foo{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type ![[TYPE2:[0-9]+]] +} + + +// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEEE"} +// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type1EE"} diff --git a/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs b/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs new file mode 100644 index 0000000000000..27e5a62816fcb --- /dev/null +++ b/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs @@ -0,0 +1,21 @@ +// Verifies that casting a method to a function pointer works. +// +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0 +//@ run-pass + +trait Trait1 { + fn foo(&self); +} + +struct Type1; + +impl Trait1 for Type1 { + fn foo(&self) {} +} + +fn main() { + let type1 = Type1 {}; + let f = ::foo; + f(&type1); +}