Skip to content

Commit

Permalink
CFI: Strip auto traits from Virtual receivers
Browse files Browse the repository at this point in the history
As the instance being called is behind a vtable, it cannot depend on
auto traits on the receiver (unless the principal trait requires them,
in which case the additional constraint is not needed).

Removing this causes the type signature of the `Virtual` instance to
match the type signature of the `CfiShim`-wrapped entry in the vtable.
  • Loading branch information
maurer committed Mar 4, 2024
1 parent 8045a58 commit 73b583c
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 5 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
//
let virtual_drop = Instance {
def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0),
args: drop_fn.args,
args: bx.tcx().strip_receiver_auto(drop_fn.args),
};
debug!("ty = {:?}", ty);
debug!("drop_fn = {:?}", drop_fn);
Expand Down Expand Up @@ -535,7 +535,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// SO THEN WE CAN USE THE ABOVE CODE.
let virtual_drop = Instance {
def: ty::InstanceDef::Virtual(drop_fn.def_id(), 0),
args: drop_fn.args,
args: bx.tcx().strip_receiver_auto(drop_fn.args),
};
debug!("ty = {:?}", ty);
debug!("drop_fn = {:?}", drop_fn);
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2256,6 +2256,13 @@ rustc_queries! {
query trait_object_ty(trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
desc { "Compute the trait object type for calling a method on a trait" }
}

/// Strip auto traits off the first parameter in the parametr list. Intended for use when
/// constructing `InstanceDef::Virtual`, as auto traits won't be part of the vtable's `Self`
/// types.
query strip_receiver_auto(args: ty::GenericArgsRef<'tcx>) -> ty::GenericArgsRef<'tcx> {
desc { "Strip auto traits off the first type arg" }
}
}

rustc_query_append! { define_callbacks! }
Expand Down
26 changes: 23 additions & 3 deletions compiler/rustc_ty_utils/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers;
use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError};
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, Instance, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::sym;
use rustc_trait_selection::traits;
use traits::{translate_args, Reveal};
Expand Down Expand Up @@ -199,7 +199,7 @@ fn resolve_associated_item<'tcx>(
traits::get_vtable_index_of_object_method(tcx, *vtable_base, trait_item_id).map(
|index| Instance {
def: ty::InstanceDef::Virtual(trait_item_id, index),
args: rcvr_args,
args: tcx.strip_receiver_auto(rcvr_args),
},
)
}
Expand Down Expand Up @@ -339,6 +339,26 @@ fn resolve_associated_item<'tcx>(
})
}

fn strip_receiver_auto<'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 {
bug!("Tried to strip auto traits from non-dynamic type {ty}");
};
let filtered_preds =
if preds.principal().is_some() {
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
!matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
}))
} else {
ty::List::empty()
};
let new_rcvr = Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind);
tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1))
}

pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { resolve_instance, ..*providers };
*providers = Providers { resolve_instance, strip_receiver_auto, ..*providers };
}
17 changes: 17 additions & 0 deletions tests/ui/sanitizer/cfi-marker-trait-objects.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Test that we can promote closures / fns to trait objects, and call them despite a marker trait.

//@ needs-sanitizer-cfi
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
//@ compile-flags: -C codegen-units=1 -C opt-level=0
//@ run-pass


fn foo() {}

static FOO: &'static (dyn Fn() + Sync) = &foo;
static BAR: &(dyn Fn() -> i32 + Sync) = &|| 3;

fn main() {
FOO();
BAR();
}

0 comments on commit 73b583c

Please sign in to comment.