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 9fe373481f5ae..11148f1f21c58 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 @@ -1047,29 +1047,36 @@ pub fn transform_fnabi<'tcx>( // pointers. return transform_fn_trait_fnabi(tcx, fn_abi, options); } else { - // Replace the concrete self by a reference to a trait object (i.e., perform - // type erasure). Non Fn trait objects are transformed into their identities - // in transform_predicate. - 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, - ), - ) + 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). Non Fn trait objects are transformed into their identities + // in transform_predicate. + 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(); 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); +}