Skip to content

Commit eec30e9

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 536606b commit eec30e9

File tree

2 files changed

+85
-10
lines changed

2 files changed

+85
-10
lines changed

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -1112,8 +1112,10 @@ pub fn typeid_for_instance<'tcx>(
11121112
mut instance: Instance<'tcx>,
11131113
options: TypeIdOptions,
11141114
) -> String {
1115-
if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
1116-
instance.args = strip_receiver_auto(tcx, instance.args)
1115+
if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
1116+
let upcast_ty = upcast(tcx, def_id, instance.args);
1117+
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
1118+
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
11171119
}
11181120

11191121
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
@@ -1159,15 +1161,11 @@ pub fn typeid_for_instance<'tcx>(
11591161
typeid_for_fnabi(tcx, fn_abi, options)
11601162
}
11611163

1162-
fn strip_receiver_auto<'tcx>(
1163-
tcx: TyCtxt<'tcx>,
1164-
args: ty::GenericArgsRef<'tcx>,
1165-
) -> ty::GenericArgsRef<'tcx> {
1166-
let ty = args.type_at(0);
1164+
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
11671165
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
11681166
bug!("Tried to strip auto traits from non-dynamic type {ty}");
11691167
};
1170-
let new_rcvr = if preds.principal().is_some() {
1168+
if preds.principal().is_some() {
11711169
let filtered_preds =
11721170
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
11731171
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
@@ -1178,8 +1176,7 @@ fn strip_receiver_auto<'tcx>(
11781176
// about it. This technically discards the knowledge that it was a type that was made
11791177
// into a trait object at some point, but that's not a lot.
11801178
tcx.types.unit
1181-
};
1182-
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
1179+
}
11831180
}
11841181

11851182
fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
@@ -1214,3 +1211,13 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc
12141211
);
12151212
Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn)
12161213
}
1214+
1215+
fn upcast<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
1216+
let self_ty = args.type_at(0);
1217+
let Some(trait_id) = tcx.trait_of_item(def_id) else {
1218+
// If it's a virtual call to `drop_in_place`, it won't be on a trait.
1219+
return self_ty;
1220+
};
1221+
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, args);
1222+
trait_object_ty(tcx, ty::Binder::dummy(trait_ref))
1223+
}

tests/ui/sanitizer/cfi-supertraits.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
type P1;
13+
fn p1(&self) -> Self::P1;
14+
}
15+
16+
trait Parent2 {
17+
type P2;
18+
fn p2(&self) -> Self::P2;
19+
}
20+
21+
trait Child : Parent1 + Parent2 {
22+
type C;
23+
fn c(&self) -> Self::C;
24+
}
25+
26+
struct Foo;
27+
28+
impl Parent1 for Foo {
29+
type P1 = u16;
30+
fn p1(&self) -> Self::P1 {
31+
println!("p1");
32+
1
33+
}
34+
}
35+
36+
impl Parent2 for Foo {
37+
type P2 = u32;
38+
fn p2(&self) -> Self::P2 {
39+
println!("p2");
40+
2
41+
}
42+
}
43+
44+
impl Child for Foo {
45+
type C = u8;
46+
fn c(&self) -> Self::C {
47+
println!("c");
48+
0
49+
}
50+
}
51+
52+
fn main() {
53+
// Child can access its own methods and super methods.
54+
let x = &Foo as &dyn Child<C=u8,P1=u16,P2=u32>;
55+
x.c();
56+
x.p1();
57+
x.p2();
58+
// Parents can be created and access their methods.
59+
let y = &Foo as &dyn Parent1<P1=u16>;
60+
y.p1();
61+
let z = &Foo as &dyn Parent2<P2=u32>;
62+
z.p2();
63+
// Trait upcasting works
64+
let x1 = x as &dyn Parent1<P1=u16>;
65+
x1.p1();
66+
let x2 = x as &dyn Parent2<P2=u32>;
67+
x2.p2();
68+
}

0 commit comments

Comments
 (0)