Skip to content

Commit d46dfa2

Browse files
committed
detect bad vptrs on dyn calls
1 parent 9927b31 commit d46dfa2

File tree

1 file changed

+46
-15
lines changed

1 file changed

+46
-15
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

+46-15
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
519519
}
520520
}
521521
// cannot use the shim here, because that will only result in infinite recursion
522-
ty::InstanceDef::Virtual(_, idx) => {
522+
ty::InstanceDef::Virtual(def_id, idx) => {
523523
let mut args = args.to_vec();
524524
// We have to implement all "object safe receivers". So we have to go search for a
525525
// pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively
@@ -552,22 +552,53 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
552552
}
553553
}
554554
};
555-
// Find and consult vtable. The type now could be something like RcBox<dyn Trait>,
556-
// i.e., it is still not necessarily `ty::Dynamic` (so we cannot use
557-
// `place.vtable()`), but it should have a `dyn Trait` tail.
558-
assert!(matches!(
559-
self.tcx
560-
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env)
561-
.kind(),
562-
ty::Dynamic(..)
563-
));
564-
let vtable = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?;
565-
let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vtable)?.get(idx).copied() else {
555+
// Obtain the underlying trait we are working on.
556+
let receiver_tail = self
557+
.tcx
558+
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
559+
let ty::Dynamic(data, ..) = receiver_tail.kind() else {
560+
span_bug!(self.cur_span(), "dyanmic call on non-`dyn` type {}", receiver_tail)
561+
};
562+
563+
// Get the required information from the vtable.
564+
let vptr = self.scalar_to_ptr(receiver_place.meta.unwrap_meta())?;
565+
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
566+
if dyn_trait != data.principal() {
566567
throw_ub_format!(
567-
"calling index {idx} of vtable {vtable} but \
568-
that vtable is too small or does not have a method at that index"
569-
)
568+
"`dyn` call on a pointer whose vtable does not match its type"
569+
);
570+
}
571+
572+
// Now determine the actual method to call. We can do that in two different ways and
573+
// compare them to ensure everything fits.
574+
let ty::VtblEntry::Method(fn_inst) = self.get_vtable_entries(vptr)?[idx] else {
575+
span_bug!(self.cur_span(), "dyn call index points at something that is not a method")
570576
};
577+
if cfg!(debug_assertions) {
578+
let tcx = *self.tcx;
579+
580+
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
581+
let virtual_trait_ref =
582+
ty::TraitRef::from_method(tcx, trait_def_id, instance.substs);
583+
assert_eq!(
584+
receiver_tail,
585+
virtual_trait_ref.self_ty(),
586+
"mismatch in underlying dyn trait computation within Miri and MIR building",
587+
);
588+
let existential_trait_ref =
589+
ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
590+
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
591+
592+
let concrete_method = Instance::resolve(
593+
tcx,
594+
self.param_env,
595+
def_id,
596+
instance.substs.rebase_onto(tcx, trait_def_id, concrete_trait_ref.substs),
597+
)
598+
.unwrap()
599+
.unwrap();
600+
assert_eq!(fn_inst, concrete_method);
601+
}
571602

572603
// `*mut receiver_place.layout.ty` is almost the layout that we
573604
// want for args[0]: We have to project to field 0 because we want

0 commit comments

Comments
 (0)