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`.
This commit is contained in:
parent
d779a7a25f
commit
d301f40c84
@ -1140,8 +1140,17 @@ pub fn typeid_for_instance<'tcx>(
|
||||
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
|
||||
let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
|
||||
instance.args = tcx.mk_args_trait(self_ty, List::empty());
|
||||
} else if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
|
||||
instance.args = strip_receiver_auto(tcx, instance.args);
|
||||
} else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
|
||||
let upcast_ty = match tcx.trait_of_item(def_id) {
|
||||
Some(trait_id) => trait_object_ty(
|
||||
tcx,
|
||||
ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)),
|
||||
),
|
||||
// drop_in_place won't have a defining trait, skip the upcast
|
||||
None => instance.args.type_at(0),
|
||||
};
|
||||
let stripped_ty = strip_receiver_auto(tcx, upcast_ty);
|
||||
instance.args = tcx.mk_args_trait(stripped_ty, instance.args.into_iter().skip(1));
|
||||
}
|
||||
|
||||
if let Some(impl_id) = tcx.impl_of_method(instance.def_id())
|
||||
@ -1190,15 +1199,11 @@ pub fn typeid_for_instance<'tcx>(
|
||||
typeid_for_fnabi(tcx, fn_abi, options)
|
||||
}
|
||||
|
||||
fn strip_receiver_auto<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
args: ty::GenericArgsRef<'tcx>,
|
||||
) -> ty::GenericArgsRef<'tcx> {
|
||||
let ty = args.type_at(0);
|
||||
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
|
||||
bug!("Tried to strip auto traits from non-dynamic type {ty}");
|
||||
};
|
||||
let new_rcvr = if preds.principal().is_some() {
|
||||
if preds.principal().is_some() {
|
||||
let filtered_preds =
|
||||
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
|
||||
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
|
||||
@ -1209,8 +1214,7 @@ fn strip_receiver_auto<'tcx>(
|
||||
// about it. This technically discards the knowledge that it was a type that was made
|
||||
// into a trait object at some point, but that's not a lot.
|
||||
tcx.types.unit
|
||||
};
|
||||
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx), ret)]
|
||||
|
73
tests/ui/sanitizer/cfi-supertraits.rs
Normal file
73
tests/ui/sanitizer/cfi-supertraits.rs
Normal file
@ -0,0 +1,73 @@
|
||||
#![feature(trait_upcasting)]
|
||||
// Check that super-traits are callable.
|
||||
|
||||
//@ revisions: cfi kcfi
|
||||
// FIXME(#122848) Remove only-linux once OSX CFI binaries work
|
||||
//@ only-linux
|
||||
//@ [cfi] needs-sanitizer-cfi
|
||||
//@ [kcfi] needs-sanitizer-kcfi
|
||||
//@ compile-flags: -C target-feature=-crt-static
|
||||
//@ [cfi] compile-flags: -C codegen-units=1 -C lto -C prefer-dynamic=off -C opt-level=0
|
||||
//@ [cfi] compile-flags: -Z sanitizer=cfi
|
||||
//@ [kcfi] compile-flags: -Z sanitizer=kcfi
|
||||
//@ [kcfi] compile-flags: -C panic=abort -C prefer-dynamic=off
|
||||
//@ run-pass
|
||||
|
||||
trait Parent1 {
|
||||
type P1;
|
||||
fn p1(&self) -> Self::P1;
|
||||
}
|
||||
|
||||
trait Parent2 {
|
||||
type P2;
|
||||
fn p2(&self) -> Self::P2;
|
||||
}
|
||||
|
||||
trait Child : Parent1 + Parent2 {
|
||||
type C;
|
||||
fn c(&self) -> Self::C;
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Parent1 for Foo {
|
||||
type P1 = u16;
|
||||
fn p1(&self) -> Self::P1 {
|
||||
println!("p1");
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl Parent2 for Foo {
|
||||
type P2 = u32;
|
||||
fn p2(&self) -> Self::P2 {
|
||||
println!("p2");
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
impl Child for Foo {
|
||||
type C = u8;
|
||||
fn c(&self) -> Self::C {
|
||||
println!("c");
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Child can access its own methods and super methods.
|
||||
let x = &Foo as &dyn Child<C=u8,P1=u16,P2=u32>;
|
||||
x.c();
|
||||
x.p1();
|
||||
x.p2();
|
||||
// Parents can be created and access their methods.
|
||||
let y = &Foo as &dyn Parent1<P1=u16>;
|
||||
y.p1();
|
||||
let z = &Foo as &dyn Parent2<P2=u32>;
|
||||
z.p2();
|
||||
// Trait upcasting works
|
||||
let x1 = x as &dyn Parent1<P1=u16>;
|
||||
x1.p1();
|
||||
let x2 = x as &dyn Parent2<P2=u32>;
|
||||
x2.p2();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user