Skip to content

Commit

Permalink
CFI: Fix methods as function pointer cast
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rcvalle committed Mar 23, 2024
1 parent cbcb16d commit 66b2708
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 20 deletions.
10 changes: 10 additions & 0 deletions compiler/rustc_codegen_llvm/src/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_symbol_mangling/src/typeid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ bitflags! {
const GENERALIZE_POINTERS = 1;
const GENERALIZE_REPR_C = 2;
const NORMALIZE_INTEGERS = 4;
const NO_TYPE_ERASURE = 8;
}
}

Expand Down
46 changes: 26 additions & 20 deletions compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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"}
21 changes: 21 additions & 0 deletions tests/ui/sanitizer/cfi-method-fn-ptr-cast.rs
Original file line number Diff line number Diff line change
@@ -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 = <Type1 as Trait1>::foo;
f(&type1);
}

0 comments on commit 66b2708

Please sign in to comment.