Skip to content

Commit 62fe784

Browse files
committed
CFI: Encode Virtual calls as calls through the defining trait
For example, if `trait Foo: Bar`, and we try to call a method from `Bar` on `dyn Foo`, encode the callsite as passing a `dyn Bar`, not a `dyn Foo`.
1 parent c84586c commit 62fe784

File tree

2 files changed

+75
-9
lines changed

2 files changed

+75
-9
lines changed

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -1114,8 +1114,10 @@ pub fn typeid_for_instance<'tcx>(
11141114
mut instance: Instance<'tcx>,
11151115
options: TypeIdOptions,
11161116
) -> String {
1117-
if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
1118-
instance.args = strip_receiver_auto(tcx, instance.args)
1117+
if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
1118+
let upcast_ty = upcast(tcx, def_id, instance.args);
1119+
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
1120+
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
11191121
}
11201122

11211123
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
@@ -1146,11 +1148,7 @@ pub fn typeid_for_instance<'tcx>(
11461148
typeid_for_fnabi(tcx, fn_abi, options)
11471149
}
11481150

1149-
fn strip_receiver_auto<'tcx>(
1150-
tcx: TyCtxt<'tcx>,
1151-
args: ty::GenericArgsRef<'tcx>,
1152-
) -> ty::GenericArgsRef<'tcx> {
1153-
let ty = args.type_at(0);
1151+
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
11541152
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
11551153
bug!("Tried to strip auto traits from non-dynamic type {ty}");
11561154
};
@@ -1162,8 +1160,7 @@ fn strip_receiver_auto<'tcx>(
11621160
} else {
11631161
ty::List::empty()
11641162
};
1165-
let new_rcvr = Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind);
1166-
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
1163+
Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind)
11671164
}
11681165

11691166
fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
@@ -1195,3 +1192,13 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc
11951192
tcx.mk_poly_existential_predicates_from_iter(iter::once(principal_pred).chain(assoc_preds));
11961193
Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn)
11971194
}
1195+
1196+
fn upcast<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
1197+
let self_ty = args.type_at(0);
1198+
let Some(trait_id) = tcx.trait_of_item(def_id) else {
1199+
// If it's a virtual call to `drop_in_place`, it won't be on a trait.
1200+
return self_ty;
1201+
};
1202+
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, args);
1203+
trait_object_ty(tcx, ty::Binder::dummy(trait_ref))
1204+
}

tests/ui/sanitizer/cfi-supertraits.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#![feature(trait_upcasting)]
2+
// Check that super-traits are callable.
3+
4+
//@ needs-sanitizer-cfi
5+
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
6+
//@ only-linux
7+
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
8+
//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0
9+
//@ run-pass
10+
11+
trait Parent1 {
12+
fn p1(&self);
13+
}
14+
15+
trait Parent2 {
16+
fn p2(&self);
17+
}
18+
19+
trait Child : Parent1 + Parent2 {
20+
fn c(&self);
21+
}
22+
23+
struct Foo;
24+
25+
impl Parent1 for Foo {
26+
fn p1(&self) {
27+
println!("p1");
28+
}
29+
}
30+
31+
impl Parent2 for Foo {
32+
fn p2(&self) {
33+
println!("p2");
34+
}
35+
}
36+
37+
impl Child for Foo {
38+
fn c(&self) {
39+
println!("c");
40+
}
41+
}
42+
43+
fn main() {
44+
// Child can access its own methods and super methods.
45+
let x = &Foo as &dyn Child;
46+
x.c();
47+
x.p1();
48+
x.p2();
49+
// Parents can be created and access their methods.
50+
let y = &Foo as &dyn Parent1;
51+
y.p1();
52+
let z = &Foo as &dyn Parent2;
53+
z.p2();
54+
// Trait upcasting works
55+
let x1 = x as &dyn Parent1;
56+
x1.p1();
57+
let x2 = x as &dyn Parent2;
58+
x2.p2();
59+
}

0 commit comments

Comments
 (0)