Skip to content

Commit 74448d1

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 d779a7a commit 74448d1

File tree

2 files changed

+89
-10
lines changed

2 files changed

+89
-10
lines changed

compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs

+17-10
Original file line numberDiff line numberDiff line change
@@ -1140,8 +1140,10 @@ pub fn typeid_for_instance<'tcx>(
11401140
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
11411141
let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
11421142
instance.args = tcx.mk_args_trait(self_ty, List::empty());
1143-
} else if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
1144-
instance.args = strip_receiver_auto(tcx, instance.args);
1143+
} else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
1144+
let upcast_ty = upcast(tcx, def_id, instance.args);
1145+
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
1146+
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
11451147
}
11461148

11471149
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
@@ -1190,15 +1192,11 @@ pub fn typeid_for_instance<'tcx>(
11901192
typeid_for_fnabi(tcx, fn_abi, options)
11911193
}
11921194

1193-
fn strip_receiver_auto<'tcx>(
1194-
tcx: TyCtxt<'tcx>,
1195-
args: ty::GenericArgsRef<'tcx>,
1196-
) -> ty::GenericArgsRef<'tcx> {
1197-
let ty = args.type_at(0);
1195+
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
11981196
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
11991197
bug!("Tried to strip auto traits from non-dynamic type {ty}");
12001198
};
1201-
let new_rcvr = if preds.principal().is_some() {
1199+
if preds.principal().is_some() {
12021200
let filtered_preds =
12031201
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
12041202
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
@@ -1209,8 +1207,7 @@ fn strip_receiver_auto<'tcx>(
12091207
// about it. This technically discards the knowledge that it was a type that was made
12101208
// into a trait object at some point, but that's not a lot.
12111209
tcx.types.unit
1212-
};
1213-
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
1210+
}
12141211
}
12151212

12161213
#[instrument(skip(tcx), ret)]
@@ -1247,3 +1244,13 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc
12471244
);
12481245
Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn)
12491246
}
1247+
1248+
fn upcast<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
1249+
let self_ty = args.type_at(0);
1250+
let Some(trait_id) = tcx.trait_of_item(def_id) else {
1251+
// If it's a virtual call to `drop_in_place`, it won't be on a trait.
1252+
return self_ty;
1253+
};
1254+
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, args);
1255+
trait_object_ty(tcx, ty::Binder::dummy(trait_ref))
1256+
}

tests/ui/sanitizer/cfi-supertraits.rs

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

0 commit comments

Comments
 (0)