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
2 changed files with 87 additions and 10 deletions
|
@ -1140,8 +1140,17 @@ pub fn typeid_for_instance<'tcx>(
|
||||||
let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
|
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);
|
let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
|
||||||
instance.args = tcx.mk_args_trait(self_ty, List::empty());
|
instance.args = tcx.mk_args_trait(self_ty, List::empty());
|
||||||
} else if matches!(instance.def, ty::InstanceDef::Virtual(..)) {
|
} else if let ty::InstanceDef::Virtual(def_id, _) = instance.def {
|
||||||
instance.args = strip_receiver_auto(tcx, instance.args);
|
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())
|
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)
|
typeid_for_fnabi(tcx, fn_abi, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_receiver_auto<'tcx>(
|
fn strip_receiver_auto<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
args: ty::GenericArgsRef<'tcx>,
|
|
||||||
) -> ty::GenericArgsRef<'tcx> {
|
|
||||||
let ty = args.type_at(0);
|
|
||||||
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
|
let ty::Dynamic(preds, lifetime, kind) = ty.kind() else {
|
||||||
bug!("Tried to strip auto traits from non-dynamic type {ty}");
|
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 =
|
let filtered_preds =
|
||||||
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
|
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
|
||||||
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
|
!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
|
// 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.
|
// into a trait object at some point, but that's not a lot.
|
||||||
tcx.types.unit
|
tcx.types.unit
|
||||||
};
|
}
|
||||||
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(tcx), ret)]
|
#[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…
Add table
Add a link
Reference in a new issue