From 8e6b4e91b68a0921c534aa1174d27f3c6418b1e6 Mon Sep 17 00:00:00 2001 From: Ramon de C Valle Date: Fri, 22 Mar 2024 15:45:23 -0700 Subject: [PATCH] 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 | 52 +++++++++---------- compiler/rustc_symbol_mangling/src/typeid.rs | 11 ++++ .../src/typeid/typeid_itanium_cxx_abi.rs | 3 +- ...itanium-cxx-abi-method-secondary-typeid.rs | 22 ++++++++ tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs | 23 ++++++++ 5 files changed, 84 insertions(+), 27 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 1a2498c75a7..3ef8538ced3 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -18,7 +18,9 @@ use crate::llvm::AttributePlace::Function; use crate::type_::Type; use crate::value::Value; +use itertools::Itertools; use rustc_codegen_ssa::traits::TypeMembershipMethods; +use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; use rustc_symbol_mangling::typeid::{ kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, @@ -141,33 +143,31 @@ pub fn declare_fn( if self.tcx.sess.is_sanitizer_cfi_enabled() { if let Some(instance) = instance { - let typeid = typeid_for_instance(self.tcx, instance, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = - typeid_for_instance(self.tcx, instance, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = - typeid_for_instance(self.tcx, instance, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_instance( - self.tcx, - instance, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + let mut typeids = FxIndexSet::default(); + for options in [ + TypeIdOptions::GENERALIZE_POINTERS, + TypeIdOptions::NORMALIZE_INTEGERS, + TypeIdOptions::NO_SELF_TYPE_ERASURE, + ] + .into_iter() + .powerset() + .map(TypeIdOptions::from_iter) + { + let typeid = typeid_for_instance(self.tcx, instance, options); + if typeids.insert(typeid.clone()) { + self.add_type_metadata(llfn, typeid); + } + } } else { - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty()); - self.set_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS); - self.add_type_metadata(llfn, typeid); - let typeid = typeid_for_fnabi( - self.tcx, - fn_abi, - TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS, - ); - self.add_type_metadata(llfn, typeid); + for options in + [TypeIdOptions::GENERALIZE_POINTERS, TypeIdOptions::NORMALIZE_INTEGERS] + .into_iter() + .powerset() + .map(TypeIdOptions::from_iter) + { + let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + self.add_type_metadata(llfn, typeid); + } } } diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index 1d28d2b732f..fc1e8e46e8d 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -13,9 +13,20 @@ /// Options for typeid_for_fnabi. #[derive(Clone, Copy, Debug)] pub struct TypeIdOptions: u32 { + /// Generalizes pointers for compatibility with Clang + /// `-fsanitize-cfi-icall-generalize-pointers` option for cross-language LLVM CFI and KCFI + /// support. const GENERALIZE_POINTERS = 1; + /// Generalizes repr(C) user-defined type for extern function types with the "C" calling + /// convention (or extern types) for cross-language LLVM CFI and KCFI support. const GENERALIZE_REPR_C = 2; + /// Normalizes integers for compatibility with Clang + /// `-fsanitize-cfi-icall-experimental-normalize-integers` option for cross-language LLVM + /// CFI and KCFI support. const NORMALIZE_INTEGERS = 4; + /// Do not perform self type erasure for attaching a secondary type id to methods with their + /// concrete self so they can be used as function pointers. + const NO_SELF_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 3101015281b..6e670f339fc 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 @@ -1116,7 +1116,8 @@ pub fn typeid_for_instance<'tcx>( instance.args = strip_receiver_auto(tcx, instance.args) } - if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) + if !options.contains(EncodeTyOptions::NO_SELF_TYPE_ERASURE) + && let Some(impl_id) = tcx.impl_of_method(instance.def_id()) && let Some(trait_ref) = tcx.impl_trait_ref(impl_id) { let impl_method = tcx.associated_item(instance.def_id()); 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 00000000000..671db563dde --- /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 ![[TYPE2:[0-9]+]] !type !{{[0-9]+}} !type !{{[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 00000000000..273b8785fae --- /dev/null +++ b/tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs @@ -0,0 +1,23 @@ +// Verifies that casting a method to a function pointer works. +// +// FIXME(#122848): Remove only-linux when fixed. +//@ only-linux +//@ needs-sanitizer-cfi +//@ compile-flags: -Clto -Copt-level=0 -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi +//@ 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); +}