Skip to content

Commit 88c921c

Browse files
committed
CFI: Introduce CFI shims
Indirect calls through vtables (trait objects or drop_in_place) expect to have a type based on `dyn Trait` at the call-site. The actual implementations have types based on `MyImplType`. These shims allow the insertion of an explicit cast at the beginning of any instance, allowing a different type to be assigned. These shims function for both CFI and KCFI, as they have a single principal type.
1 parent e34e710 commit 88c921c

File tree

19 files changed

+311
-22
lines changed

19 files changed

+311
-22
lines changed

compiler/rustc_const_eval/src/interpret/terminator.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
547547
| ty::InstanceDef::CloneShim(..)
548548
| ty::InstanceDef::FnPtrAddrShim(..)
549549
| ty::InstanceDef::ThreadLocalShim(..)
550+
| ty::InstanceDef::CfiShim { .. }
550551
| ty::InstanceDef::Item(_) => {
551552
// We need MIR for this fn
552553
let Some((body, instance)) = M::find_mir_or_eval_fn(

compiler/rustc_middle/src/mir/mono.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,8 @@ impl<'tcx> CodegenUnit<'tcx> {
406406
| InstanceDef::DropGlue(..)
407407
| InstanceDef::CloneShim(..)
408408
| InstanceDef::ThreadLocalShim(..)
409-
| InstanceDef::FnPtrAddrShim(..) => None,
409+
| InstanceDef::FnPtrAddrShim(..)
410+
| InstanceDef::CfiShim { .. } => None,
410411
}
411412
}
412413
MonoItem::Static(def_id) => def_id.as_local().map(Idx::index),

compiler/rustc_middle/src/mir/visit.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,21 @@ macro_rules! make_mir_visitor {
939939
// FIXME(eddyb) use a better `TyContext` here.
940940
self.visit_ty($(& $mutability *)? ty, TyContext::Location(location));
941941
}
942+
ty::InstanceDef::CfiShim { target_instance, invoke_ty } => {
943+
self.visit_ty($(& $mutability *)? invoke_ty, TyContext::Location(location));
944+
let $($mutability)? local_target_instance = {
945+
$(
946+
let $mutability _unused = ();
947+
*
948+
)?
949+
*target_instance
950+
};
951+
self.visit_instance_def($(& $mutability)? local_target_instance);
952+
$(
953+
*target_instance = self.tcx().arena.alloc(local_target_instance);
954+
let $mutability _unused = ();
955+
)?
956+
}
942957
}
943958
}
944959

compiler/rustc_middle/src/ty/codec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ impl_arena_copy_decoder! {<'tcx>
526526
rustc_span::def_id::LocalDefId,
527527
(rustc_middle::middle::exported_symbols::ExportedSymbol<'tcx>, rustc_middle::middle::exported_symbols::SymbolExportInfo),
528528
ty::DeducedParamAttrs,
529+
ty::InstanceDef<'tcx>,
529530
}
530531

531532
#[macro_export]

compiler/rustc_middle/src/ty/instance.rs

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
22
use crate::ty::print::{FmtPrinter, Printer};
33
use crate::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
4-
use crate::ty::{EarlyBinder, GenericArgs, GenericArgsRef, TypeVisitableExt};
4+
use crate::ty::{EarlyBinder, GenericArgs, GenericArgsRef, Lift, TypeVisitableExt};
55
use rustc_errors::ErrorGuaranteed;
66
use rustc_hir as hir;
77
use rustc_hir::def::Namespace;
@@ -135,14 +135,27 @@ pub enum InstanceDef<'tcx> {
135135
///
136136
/// The `DefId` is for `FnPtr::addr`, the `Ty` is the type `T`.
137137
FnPtrAddrShim(DefId, Ty<'tcx>),
138+
139+
/// Typecast shim which replaces the `Self` type with the provided type.
140+
/// This is used in vtable calls, where the type of `Self` is abstract as of the time of
141+
/// the call.
142+
///
143+
/// For `target_instance` which are generatable shims, this is done via inserting a cast at the
144+
/// beginning of the shim generated by that instance.
145+
/// For `Item`s, we instead build a direct call shim with that cast inserted.
146+
CfiShim { target_instance: &'tcx InstanceDef<'tcx>, invoke_ty: Ty<'tcx> },
138147
}
139148

140149
impl<'tcx> Instance<'tcx> {
141150
/// Returns the `Ty` corresponding to this `Instance`, with generic instantiations applied and
142151
/// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization.
143152
pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
144-
let ty = tcx.type_of(self.def.def_id());
145-
tcx.instantiate_and_normalize_erasing_regions(self.args, param_env, ty)
153+
let args = if let InstanceDef::CfiShim { invoke_ty, .. } = self.def {
154+
tcx.mk_args_trait(invoke_ty, (*self.args).into_iter().skip(1))
155+
} else {
156+
self.args
157+
};
158+
tcx.instantiate_and_normalize_erasing_regions(args, param_env, tcx.type_of(self.def_id()))
146159
}
147160

148161
/// Finds a crate that contains a monomorphization of this instance that
@@ -198,6 +211,7 @@ impl<'tcx> InstanceDef<'tcx> {
198211
| InstanceDef::DropGlue(def_id, _)
199212
| InstanceDef::CloneShim(def_id, _)
200213
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
214+
InstanceDef::CfiShim { target_instance, .. } => target_instance.def_id(),
201215
}
202216
}
203217

@@ -209,6 +223,7 @@ impl<'tcx> InstanceDef<'tcx> {
209223
Some(def_id)
210224
}
211225
InstanceDef::VTableShim(..)
226+
| InstanceDef::CfiShim { .. }
212227
| InstanceDef::ReifyShim(..)
213228
| InstanceDef::FnPtrShim(..)
214229
| InstanceDef::Virtual(..)
@@ -319,6 +334,9 @@ impl<'tcx> InstanceDef<'tcx> {
319334
| InstanceDef::ReifyShim(..)
320335
| InstanceDef::Virtual(..)
321336
| InstanceDef::VTableShim(..) => true,
337+
InstanceDef::CfiShim { target_instance, .. } => {
338+
target_instance.has_polymorphic_mir_body()
339+
}
322340
}
323341
}
324342

@@ -360,6 +378,10 @@ fn fmt_instance_def(f: &mut fmt::Formatter<'_>, instance_def: &InstanceDef<'_>)
360378
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
361379
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"),
362380
InstanceDef::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"),
381+
InstanceDef::CfiShim { invoke_ty, target_instance } => {
382+
fmt_instance_def(f, target_instance)?;
383+
write!(f, " - cfi-shim({invoke_ty})")
384+
}
363385
}
364386
}
365387

@@ -600,6 +622,75 @@ impl<'tcx> Instance<'tcx> {
600622
Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
601623
}
602624

625+
pub fn cfi_shim(
626+
mut self,
627+
tcx: TyCtxt<'tcx>,
628+
invoke_trait: Option<ty::PolyTraitRef<'tcx>>,
629+
) -> ty::Instance<'tcx> {
630+
if tcx.sess.cfi_shims() {
631+
let invoke_ty = if let Some(poly_trait_ref) = invoke_trait {
632+
tcx.trait_object_ty(poly_trait_ref)
633+
} else {
634+
Ty::new_dynamic(tcx, ty::List::empty(), tcx.lifetimes.re_erased, ty::Dyn)
635+
};
636+
if tcx.is_closure_like(self.def.def_id()) {
637+
// Closures don't have a fn_sig and can't be called directly.
638+
// Adjust it into a call through
639+
// `Fn`/`FnMut`/`FnOnce`/`AsyncFn`/`AsyncFnMut`/`AsyncFnOnce`/`Coroutine`
640+
// based on its receiver.
641+
let ty::TyKind::Dynamic(pep, _, _) = invoke_ty.kind() else {
642+
bug!("Closure-like with non-dynamic invoke_ty {invoke_ty}")
643+
};
644+
let Some(fn_trait) = pep.principal_def_id() else {
645+
bug!("Closure-like with no principal trait on invoke_ty {invoke_ty}")
646+
};
647+
let call = tcx
648+
.associated_items(fn_trait)
649+
.in_definition_order()
650+
.find(|it| it.kind == ty::AssocKind::Fn)
651+
.expect("No call-family function on closure-like principal trait?")
652+
.def_id;
653+
654+
let self_ty = self.ty(tcx, ty::ParamEnv::reveal_all());
655+
let tupled_inputs_ty =
656+
self.args.as_closure().sig().map_bound(|sig| sig.inputs()[0]);
657+
let tupled_inputs_ty = tcx.instantiate_bound_regions_with_erased(tupled_inputs_ty);
658+
self.args = tcx.mk_args_trait(self_ty, [tupled_inputs_ty.into()]);
659+
self.def = InstanceDef::Item(call);
660+
} else if let Some(impl_id) = tcx.impl_of_method(self.def.def_id()) {
661+
// Trait methods will have a Self polymorphic parameter, where the concreteized
662+
// implementatation will not. We need to walk back to the more general trait method
663+
// so that we can swap out Self when generating a type signature.
664+
let Some(trait_ref) = tcx.impl_trait_ref(impl_id) else {
665+
bug!("When trying to rewrite the type on {self}, found inherent impl method")
666+
};
667+
let trait_ref = trait_ref.instantiate(tcx, self.args);
668+
669+
let method_id = tcx
670+
.impl_item_implementor_ids(impl_id)
671+
.items()
672+
.filter_map(|(trait_method, impl_method)| {
673+
(*impl_method == self.def.def_id()).then_some(*trait_method)
674+
})
675+
.min()
676+
.unwrap();
677+
self.def = InstanceDef::Item(method_id);
678+
self.args = trait_ref.args;
679+
}
680+
// At this point, we should have gauranteed that the first item in the args list is
681+
// the dispatch type. We can't check for Self, because `drop_in_place` takes `T`.
682+
self.def = InstanceDef::CfiShim {
683+
target_instance: (&self.def).lift_to_tcx(tcx).expect("Could not lift for shimming"),
684+
invoke_ty,
685+
};
686+
}
687+
self
688+
}
689+
690+
pub fn force_thin_self(&self) -> bool {
691+
matches!(self.def, InstanceDef::Virtual(..) | InstanceDef::CfiShim { .. })
692+
}
693+
603694
#[instrument(level = "debug", skip(tcx), ret)]
604695
pub fn fn_once_adapter_instance(
605696
tcx: TyCtxt<'tcx>,

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1722,7 +1722,8 @@ impl<'tcx> TyCtxt<'tcx> {
17221722
| ty::InstanceDef::DropGlue(..)
17231723
| ty::InstanceDef::CloneShim(..)
17241724
| ty::InstanceDef::ThreadLocalShim(..)
1725-
| ty::InstanceDef::FnPtrAddrShim(..) => self.mir_shims(instance),
1725+
| ty::InstanceDef::FnPtrAddrShim(..)
1726+
| ty::InstanceDef::CfiShim { .. } => self.mir_shims(instance),
17261727
}
17271728
}
17281729

compiler/rustc_middle/src/ty/structural_impls.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,14 @@ impl<'a, 'tcx> Lift<'tcx> for Term<'a> {
478478
}
479479
}
480480

481+
impl<'a, 'b, 'tcx> Lift<'tcx> for &'b ty::InstanceDef<'a> {
482+
type Lifted = &'tcx ty::InstanceDef<'tcx>;
483+
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
484+
let lifted: ty::InstanceDef<'tcx> = (*self).lift_to_tcx(tcx)?;
485+
Some(tcx.arena.alloc(lifted))
486+
}
487+
}
488+
481489
///////////////////////////////////////////////////////////////////////////
482490
// Traversal implementations.
483491

@@ -490,6 +498,22 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::AdtDef<'tcx> {
490498
}
491499
}
492500

501+
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for &ty::InstanceDef<'tcx> {
502+
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
503+
(*self).visit_with(visitor)
504+
}
505+
}
506+
507+
impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::InstanceDef<'tcx> {
508+
fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
509+
self,
510+
folder: &mut F,
511+
) -> Result<Self, F::Error> {
512+
let folded: ty::InstanceDef<'tcx> = (*self).try_fold_with(folder)?;
513+
Ok(folder.interner().arena.alloc(folded))
514+
}
515+
}
516+
493517
impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> TypeFoldable<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
494518
fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
495519
self,

compiler/rustc_mir_transform/src/inline.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,8 @@ impl<'tcx> Inliner<'tcx> {
331331
| InstanceDef::DropGlue(..)
332332
| InstanceDef::CloneShim(..)
333333
| InstanceDef::ThreadLocalShim(..)
334-
| InstanceDef::FnPtrAddrShim(..) => return Ok(()),
334+
| InstanceDef::FnPtrAddrShim(..)
335+
| InstanceDef::CfiShim { .. } => return Ok(()),
335336
}
336337

337338
if self.tcx.is_constructor(callee_def_id) {

compiler/rustc_mir_transform/src/inline/cycle.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
9090
| InstanceDef::ConstructCoroutineInClosureShim { .. }
9191
| InstanceDef::CoroutineKindShim { .. }
9292
| InstanceDef::ThreadLocalShim { .. }
93-
| InstanceDef::CloneShim(..) => {}
93+
| InstanceDef::CloneShim(..)
94+
| InstanceDef::CfiShim { .. } => {}
9495

9596
// This shim does not call any other functions, thus there can be no recursion.
9697
InstanceDef::FnPtrAddrShim(..) => continue,

compiler/rustc_mir_transform/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ mod remove_unneeded_drops;
101101
mod remove_zsts;
102102
mod required_consts;
103103
mod reveal_all;
104+
mod rewrite_receiver;
104105
mod shim;
105106
mod ssa;
106107
// This pass is public to allow external drivers to perform MIR cleanup

0 commit comments

Comments
 (0)