Skip to content

Commit 4abea48

Browse files
committed
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.
1 parent 6f146f5 commit 4abea48

File tree

5 files changed

+82
-21
lines changed

5 files changed

+82
-21
lines changed

compiler/rustc_codegen_llvm/src/declare.rs

+10
Original file line numberDiff line numberDiff line change
@@ -141,20 +141,30 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
141141

142142
if self.tcx.sess.is_sanitizer_cfi_enabled() {
143143
if let Some(instance) = instance {
144+
let mut typeids = Vec::new();
144145
let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty());
146+
typeids.push(typeid.clone());
145147
self.set_type_metadata(llfn, typeid);
146148
let typeid =
147149
typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS);
150+
typeids.push(typeid.clone());
148151
self.add_type_metadata(llfn, typeid);
149152
let typeid =
150153
typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS);
154+
typeids.push(typeid.clone());
151155
self.add_type_metadata(llfn, typeid);
152156
let typeid = typeid_for_instance(
153157
self.tcx,
154158
&instance,
155159
TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS,
156160
);
161+
typeids.push(typeid.clone());
157162
self.add_type_metadata(llfn, typeid);
163+
let typeid =
164+
typeid_for_instance(self.tcx, &instance, TypeIdOptions::NO_TYPE_ERASURE);
165+
if !typeids.contains(&typeid) {
166+
self.add_type_metadata(llfn, typeid);
167+
}
158168
} else {
159169
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty());
160170
self.set_type_metadata(llfn, typeid);

compiler/rustc_symbol_mangling/src/typeid.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ bitflags! {
1616
const GENERALIZE_POINTERS = 1;
1717
const GENERALIZE_REPR_C = 2;
1818
const NORMALIZE_INTEGERS = 4;
19+
const NO_TYPE_ERASURE = 8;
1920
}
2021
}
2122

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+28-21
Original file line numberDiff line numberDiff line change
@@ -1046,29 +1046,36 @@ pub fn transform_fnabi<'tcx>(
10461046
// pointers.
10471047
return transform_fn_trait_fnabi(tcx, fn_abi, options);
10481048
} else {
1049-
// Replace the concrete self by a reference to a trait object (i.e., perform
1050-
// type erasure). Non Fn trait objects are transformed into their identities
1051-
// in transform_predicate.
1052-
if fn_abi.args[0].layout.ty.is_mutable_ptr() {
1053-
Ty::new_mut_ref(
1054-
tcx,
1055-
tcx.lifetimes.re_erased,
1056-
new_dynamic_trait(
1057-
tcx,
1058-
trait_ref.skip_binder().def_id,
1059-
trait_ref.skip_binder().args,
1060-
),
1061-
)
1049+
if options.contains(EncodeTyOptions::NO_TYPE_ERASURE) {
1050+
// Do not perform type erasure for assigning a secondary type id to
1051+
// methods with their concrete self so they can be used as function
1052+
// pointers.
1053+
return fn_abi.clone();
10621054
} else {
1063-
Ty::new_imm_ref(
1064-
tcx,
1065-
tcx.lifetimes.re_erased,
1066-
new_dynamic_trait(
1055+
// Replace the concrete self by a reference to a trait object (i.e., perform
1056+
// type erasure). Non Fn trait objects are transformed into their identities
1057+
// in transform_predicate.
1058+
if fn_abi.args[0].layout.ty.is_mutable_ptr() {
1059+
Ty::new_mut_ref(
10671060
tcx,
1068-
trait_ref.skip_binder().def_id,
1069-
trait_ref.skip_binder().args,
1070-
),
1071-
)
1061+
tcx.lifetimes.re_erased,
1062+
new_dynamic_trait(
1063+
tcx,
1064+
trait_ref.skip_binder().def_id,
1065+
trait_ref.skip_binder().args,
1066+
),
1067+
)
1068+
} else {
1069+
Ty::new_imm_ref(
1070+
tcx,
1071+
tcx.lifetimes.re_erased,
1072+
new_dynamic_trait(
1073+
tcx,
1074+
trait_ref.skip_binder().def_id,
1075+
trait_ref.skip_binder().args,
1076+
),
1077+
)
1078+
}
10721079
}
10731080
};
10741081
let mut fn_abi = fn_abi.clone();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Verifies that a secondary type metadata identifier is assigned to methods with their concrete
2+
// self so they can be used as function pointers.
3+
//
4+
//@ needs-sanitizer-cfi
5+
//@ compile-flags: -Clto -Cno-prepopulate-passes -Copt-level=0 -Zsanitizer=cfi -Ctarget-feature=-crt-static
6+
7+
#![crate_type="lib"]
8+
9+
trait Trait1 {
10+
fn foo(&self);
11+
}
12+
13+
struct Type1;
14+
15+
impl Trait1 for Type1 {
16+
fn foo(&self) {}
17+
// CHECK: define{{.*}}3foo{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type ![[TYPE2:[0-9]+]]
18+
}
19+
20+
21+
// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}6Trait1u6regionEEE"}
22+
// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type1EE"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Verifies that casting a method to a function pointer works.
2+
//
3+
//@ needs-sanitizer-cfi
4+
//@ compile-flags: -Clto -Cprefer-dynamic=off -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0
5+
//@ run-pass
6+
7+
trait Trait1 {
8+
fn foo(&self);
9+
}
10+
11+
struct Type1;
12+
13+
impl Trait1 for Type1 {
14+
fn foo(&self) {}
15+
}
16+
17+
fn main() {
18+
let type1 = Type1 {};
19+
let f = <Type1 as Trait1>::foo;
20+
f(&type1);
21+
}

0 commit comments

Comments
 (0)