Skip to content

Commit 691a5f3

Browse files
Rollup merge of #111375 - rcvalle:rust-cfi-fix-106547, r=bjorn3
CFI: Fix SIGILL reached via trait objects Fix #106547 by transforming the concrete self into a reference to a trait object before emitting type metadata identifiers for trait methods.
2 parents 41ab8e6 + 7c7b22e commit 691a5f3

File tree

10 files changed

+247
-28
lines changed

10 files changed

+247
-28
lines changed

compiler/rustc_codegen_llvm/src/callee.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ pub fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) ->
9494
// LLVM will prefix the name with `__imp_`. Ideally, we'd like the
9595
// existing logic below to set the Storage Class, but it has an
9696
// exemption for MinGW for backwards compatability.
97-
let llfn = cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi);
97+
let llfn = cx.declare_fn(&common::i686_decorated_name(&dllimport, common::is_mingw_gnu_toolchain(&tcx.sess.target), true), fn_abi, Some(instance));
9898
unsafe { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); }
9999
llfn
100100
} else {
101-
cx.declare_fn(sym, fn_abi)
101+
cx.declare_fn(sym, fn_abi, Some(instance))
102102
};
103103
debug!("get_fn: not casting pointer!");
104104

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<
207207
)),
208208
ty::List::empty(),
209209
),
210+
None,
210211
);
211212

212213
llvm::set_linkage(llfn, llvm::Linkage::PrivateLinkage);

compiler/rustc_codegen_llvm/src/declare.rs

+47-17
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ use crate::llvm::AttributePlace::Function;
1919
use crate::type_::Type;
2020
use crate::value::Value;
2121
use rustc_codegen_ssa::traits::TypeMembershipMethods;
22-
use rustc_middle::ty::Ty;
23-
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions};
22+
use rustc_middle::ty::{Instance, Ty};
23+
use rustc_symbol_mangling::typeid::{
24+
kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance,
25+
TypeIdOptions,
26+
};
2427
use smallvec::SmallVec;
2528

2629
/// Declare a function.
@@ -116,7 +119,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
116119
///
117120
/// If there’s a value with the same name already declared, the function will
118121
/// update the declaration and return existing Value instead.
119-
pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> &'ll Value {
122+
pub fn declare_fn(
123+
&self,
124+
name: &str,
125+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
126+
instance: Option<Instance<'tcx>>,
127+
) -> &'ll Value {
120128
debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi);
121129

122130
// Function addresses in Rust are never significant, allowing functions to
@@ -132,18 +140,35 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
132140
fn_abi.apply_attrs_llfn(self, llfn);
133141

134142
if self.tcx.sess.is_sanitizer_cfi_enabled() {
135-
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty());
136-
self.set_type_metadata(llfn, typeid);
137-
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS);
138-
self.add_type_metadata(llfn, typeid);
139-
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS);
140-
self.add_type_metadata(llfn, typeid);
141-
let typeid = typeid_for_fnabi(
142-
self.tcx,
143-
fn_abi,
144-
TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS,
145-
);
146-
self.add_type_metadata(llfn, typeid);
143+
if let Some(instance) = instance {
144+
let typeid = typeid_for_instance(self.tcx, &instance, TypeIdOptions::empty());
145+
self.set_type_metadata(llfn, typeid);
146+
let typeid =
147+
typeid_for_instance(self.tcx, &instance, TypeIdOptions::GENERALIZE_POINTERS);
148+
self.add_type_metadata(llfn, typeid);
149+
let typeid =
150+
typeid_for_instance(self.tcx, &instance, TypeIdOptions::NORMALIZE_INTEGERS);
151+
self.add_type_metadata(llfn, typeid);
152+
let typeid = typeid_for_instance(
153+
self.tcx,
154+
&instance,
155+
TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS,
156+
);
157+
self.add_type_metadata(llfn, typeid);
158+
} else {
159+
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::empty());
160+
self.set_type_metadata(llfn, typeid);
161+
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::GENERALIZE_POINTERS);
162+
self.add_type_metadata(llfn, typeid);
163+
let typeid = typeid_for_fnabi(self.tcx, fn_abi, TypeIdOptions::NORMALIZE_INTEGERS);
164+
self.add_type_metadata(llfn, typeid);
165+
let typeid = typeid_for_fnabi(
166+
self.tcx,
167+
fn_abi,
168+
TypeIdOptions::GENERALIZE_POINTERS | TypeIdOptions::NORMALIZE_INTEGERS,
169+
);
170+
self.add_type_metadata(llfn, typeid);
171+
}
147172
}
148173

149174
if self.tcx.sess.is_sanitizer_kcfi_enabled() {
@@ -156,8 +181,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
156181
options.insert(TypeIdOptions::NORMALIZE_INTEGERS);
157182
}
158183

159-
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options);
160-
self.set_kcfi_type_metadata(llfn, kcfi_typeid);
184+
if let Some(instance) = instance {
185+
let kcfi_typeid = kcfi_typeid_for_instance(self.tcx, &instance, options);
186+
self.set_kcfi_type_metadata(llfn, kcfi_typeid);
187+
} else {
188+
let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options);
189+
self.set_kcfi_type_metadata(llfn, kcfi_typeid);
190+
}
161191
}
162192

163193
llfn

compiler/rustc_codegen_llvm/src/intrinsic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ fn gen_fn<'ll, 'tcx>(
772772
) -> (&'ll Type, &'ll Value) {
773773
let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty());
774774
let llty = fn_abi.llvm_type(cx);
775-
let llfn = cx.declare_fn(name, fn_abi);
775+
let llfn = cx.declare_fn(name, fn_abi, None);
776776
cx.set_frame_pointer_type(llfn);
777777
cx.apply_target_cpu_attr(llfn);
778778
// FIXME(eddyb) find a nicer way to do this.

compiler/rustc_codegen_llvm/src/mono_item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> {
5151
assert!(!instance.substs.has_infer());
5252

5353
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
54-
let lldecl = self.declare_fn(symbol_name, fn_abi);
54+
let lldecl = self.declare_fn(symbol_name, fn_abi, Some(instance));
5555
unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) };
5656
let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
5757
base::set_link_section(lldecl, attrs);

compiler/rustc_symbol_mangling/src/typeid.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
55
/// see design document in the tracking issue #89653.
66
use bitflags::bitflags;
7-
use rustc_middle::ty::{FnSig, Ty, TyCtxt};
7+
use rustc_middle::ty::{FnSig, Instance, Ty, TyCtxt};
88
use rustc_target::abi::call::FnAbi;
99
use std::hash::Hasher;
1010
use twox_hash::XxHash64;
@@ -38,6 +38,15 @@ pub fn typeid_for_fnsig<'tcx>(
3838
typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, options)
3939
}
4040

41+
/// Returns a type metadata identifier for the specified Instance.
42+
pub fn typeid_for_instance<'tcx>(
43+
tcx: TyCtxt<'tcx>,
44+
instance: &Instance<'tcx>,
45+
options: TypeIdOptions,
46+
) -> String {
47+
typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options)
48+
}
49+
4150
/// Returns a KCFI type metadata identifier for the specified FnAbi.
4251
pub fn kcfi_typeid_for_fnabi<'tcx>(
4352
tcx: TyCtxt<'tcx>,
@@ -63,3 +72,16 @@ pub fn kcfi_typeid_for_fnsig<'tcx>(
6372
hash.write(typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, options).as_bytes());
6473
hash.finish() as u32
6574
}
75+
76+
/// Returns a KCFI type metadata identifier for the specified Instance.
77+
pub fn kcfi_typeid_for_instance<'tcx>(
78+
tcx: TyCtxt<'tcx>,
79+
instance: &Instance<'tcx>,
80+
options: TypeIdOptions,
81+
) -> u32 {
82+
// A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the
83+
// xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.)
84+
let mut hash: XxHash64 = Default::default();
85+
hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes());
86+
hash.finish() as u32
87+
}

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+55-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use rustc_errors::DiagnosticMessage;
1414
use rustc_hir as hir;
1515
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
1616
use rustc_middle::ty::{
17-
self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind,
18-
Ty, TyCtxt, UintTy,
17+
self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
18+
TermKind, Ty, TyCtxt, UintTy,
1919
};
2020
use rustc_span::def_id::DefId;
2121
use rustc_span::sym;
@@ -1010,3 +1010,56 @@ pub fn typeid_for_fnsig<'tcx>(
10101010

10111011
typeid
10121012
}
1013+
1014+
/// Returns a type metadata identifier for the specified Instance using the Itanium C++ ABI with
1015+
/// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary.
1016+
pub fn typeid_for_instance<'tcx>(
1017+
tcx: TyCtxt<'tcx>,
1018+
instance: &Instance<'tcx>,
1019+
options: TypeIdOptions,
1020+
) -> String {
1021+
let fn_abi = tcx
1022+
.fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty())))
1023+
.unwrap_or_else(|instance| {
1024+
bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance)
1025+
});
1026+
1027+
// If this instance is a method and self is a reference, get the impl it belongs to
1028+
let impl_def_id = tcx.impl_of_method(instance.def_id());
1029+
if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() {
1030+
// If this impl is not an inherent impl, get the trait it implements
1031+
if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) {
1032+
// Transform the concrete self into a reference to a trait object
1033+
let existential_predicate = trait_ref.map_bound(|trait_ref| {
1034+
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
1035+
tcx, trait_ref,
1036+
))
1037+
});
1038+
let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(
1039+
existential_predicate.skip_binder(),
1040+
)]);
1041+
// Is the concrete self mutable?
1042+
let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() {
1043+
tcx.mk_mut_ref(
1044+
tcx.lifetimes.re_erased,
1045+
tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
1046+
)
1047+
} else {
1048+
tcx.mk_imm_ref(
1049+
tcx.lifetimes.re_erased,
1050+
tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
1051+
)
1052+
};
1053+
1054+
// Replace the concrete self in an fn_abi clone by the reference to a trait object
1055+
let mut fn_abi = fn_abi.clone();
1056+
// HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the
1057+
// other fields are never used.
1058+
fn_abi.args[0].layout.ty = self_ty;
1059+
1060+
return typeid_for_fnabi(tcx, &fn_abi, options);
1061+
}
1062+
}
1063+
1064+
typeid_for_fnabi(tcx, &fn_abi, options)
1065+
}

compiler/rustc_target/src/abi/call/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod x86;
2828
mod x86_64;
2929
mod x86_win64;
3030

31-
#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
31+
#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
3232
pub enum PassMode {
3333
/// Ignore the argument.
3434
///
@@ -211,7 +211,7 @@ impl Uniform {
211211
}
212212
}
213213

214-
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
214+
#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
215215
pub struct CastTarget {
216216
pub prefix: [Option<Reg>; 8],
217217
pub rest: Uniform,
@@ -458,7 +458,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
458458

459459
/// Information about how to pass an argument to,
460460
/// or return a value from, a function, under some ABI.
461-
#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
461+
#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
462462
pub struct ArgAbi<'a, Ty> {
463463
pub layout: TyAndLayout<'a, Ty>,
464464
pub mode: PassMode,
@@ -605,7 +605,7 @@ pub enum Conv {
605605
///
606606
/// I will do my best to describe this structure, but these
607607
/// comments are reverse-engineered and may be inaccurate. -NDM
608-
#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)]
608+
#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
609609
pub struct FnAbi<'a, Ty> {
610610
/// The LLVM types of each argument.
611611
pub args: Box<[ArgAbi<'a, Ty>]>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Verifies that type metadata identifiers for trait objects are emitted correctly.
2+
//
3+
// needs-sanitizer-cfi
4+
// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
5+
6+
#![crate_type="lib"]
7+
8+
trait Trait1 {
9+
fn foo(&self);
10+
}
11+
12+
struct Type1;
13+
14+
impl Trait1 for Type1 {
15+
fn foo(&self) {
16+
}
17+
}
18+
19+
pub fn foo() {
20+
let a = Type1;
21+
a.foo();
22+
// CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}}
23+
// CHECK: call <sanitizer_cfi_emit_type_metadata_trait_objects::Type1 as sanitizer_cfi_emit_type_metadata_trait_objects::Trait1>::foo
24+
}
25+
26+
pub fn bar() {
27+
let a = Type1;
28+
let b = &a as &dyn Trait1;
29+
b.foo();
30+
// CHECK-LABEL: define{{.*}}bar{{.*}}!type !{{[0-9]+}}
31+
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]")
32+
}
33+
34+
pub fn baz() {
35+
let a = Type1;
36+
let b = &a as &dyn Trait1;
37+
a.foo();
38+
b.foo();
39+
// CHECK-LABEL: define{{.*}}baz{{.*}}!type !{{[0-9]+}}
40+
// CHECK: call <sanitizer_cfi_emit_type_metadata_trait_objects::Type1 as sanitizer_cfi_emit_type_metadata_trait_objects::Trait1>::foo
41+
// CHECK: call i1 @llvm.type.test({{i8\*|ptr}} {{%f|%0|%1}}, metadata !"[[TYPE1:[[:print:]]+]]")
42+
}
43+
44+
// CHECK: !{{[0-9]+}} = !{i64 0, !"[[TYPE1]]"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Verifies that type metadata identifiers for trait objects are emitted correctly.
2+
//
3+
// revisions: aarch64 x86_64
4+
// [aarch64] compile-flags: --target aarch64-unknown-none
5+
// [aarch64] needs-llvm-components: aarch64
6+
// [x86_64] compile-flags: --target x86_64-unknown-none
7+
// [x86_64] needs-llvm-components:
8+
// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0
9+
10+
#![crate_type="lib"]
11+
#![feature(arbitrary_self_types, no_core, lang_items)]
12+
#![no_core]
13+
14+
#[lang="sized"]
15+
trait Sized { }
16+
#[lang="copy"]
17+
trait Copy { }
18+
#[lang="receiver"]
19+
trait Receiver { }
20+
#[lang="dispatch_from_dyn"]
21+
trait DispatchFromDyn<T> { }
22+
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
23+
#[lang = "unsize"]
24+
trait Unsize<T: ?Sized> { }
25+
#[lang = "coerce_unsized"]
26+
pub trait CoerceUnsized<T: ?Sized> { }
27+
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
28+
#[lang="freeze"]
29+
trait Freeze { }
30+
#[lang="drop_in_place"]
31+
fn drop_in_place_fn<T>() { }
32+
33+
trait Trait1 {
34+
fn foo(&self);
35+
}
36+
37+
struct Type1;
38+
39+
impl Trait1 for Type1 {
40+
fn foo(&self) {
41+
}
42+
}
43+
44+
pub fn foo() {
45+
let a = Type1;
46+
a.foo();
47+
// CHECK-LABEL: define{{.*}}foo{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
48+
// CHECK: call <sanitizer_kcfi_emit_type_metadata_trait_objects::Type1 as sanitizer_kcfi_emit_type_metadata_trait_objects::Trait1>::foo
49+
}
50+
51+
pub fn bar() {
52+
let a = Type1;
53+
let b = &a as &dyn Trait1;
54+
b.foo();
55+
// CHECK-LABEL: define{{.*}}bar{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
56+
// CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ]
57+
}
58+
59+
pub fn baz() {
60+
let a = Type1;
61+
let b = &a as &dyn Trait1;
62+
a.foo();
63+
b.foo();
64+
// CHECK-LABEL: define{{.*}}baz{{.*}}!{{<unknown kind #36>|kcfi_type}} !{{[0-9]+}}
65+
// CHECK: call <sanitizer_kcfi_emit_type_metadata_trait_objects::Type1 as sanitizer_kcfi_emit_type_metadata_trait_objects::Trait1>::foo
66+
// CHECK: call void %0({{\{\}\*|ptr}} align 1 {{%b\.0|%_1}}){{.*}}[ "kcfi"(i32 [[TYPE1:[[:print:]]+]]) ]
67+
}
68+
69+
// CHECK: !{{[0-9]+}} = !{i32 [[TYPE1]]}

0 commit comments

Comments
 (0)