Track reason for creating a ReifyShim
KCFI needs to be able to tell which kind of `ReifyShim` it is examining in order to decide whether to use a concrete type (`FnPtr` case) or an abstract case (`Vtable` case). You can *almost* tell this from context, but there is one case where you can't - if a trait has a method which is *not* `#[track_caller]`, with an impl that *is* `#[track_caller]`, both the vtable and a function pointer created from that method will be `ReifyShim(def_id)`. Currently, the reason is optional to ensure no additional unique `ReifyShim`s are added without KCFI on. However, the case in which an extra `ReifyShim` is created is sufficiently rare that this may be worth revisiting to reduce complexity.
This commit is contained in:
parent
93c2bace58
commit
6aa89f684e
9 changed files with 60 additions and 19 deletions
|
@ -31,6 +31,28 @@ pub struct Instance<'tcx> {
|
|||
pub args: GenericArgsRef<'tcx>,
|
||||
}
|
||||
|
||||
/// Describes why a `ReifyShim` was created. This is needed to distingish a ReifyShim created to
|
||||
/// adjust for things like `#[track_caller]` in a vtable from a `ReifyShim` created to produce a
|
||||
/// function pointer from a vtable entry.
|
||||
/// Currently, this is only used when KCFI is enabled, as only KCFI needs to treat those two
|
||||
/// `ReifyShim`s differently.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable)]
|
||||
pub enum ReifyReason {
|
||||
/// The `ReifyShim` was created to produce a function pointer. This happens when:
|
||||
/// * A vtable entry is directly converted to a function call (e.g. creating a fn ptr from a
|
||||
/// method on a `dyn` object).
|
||||
/// * A function with `#[track_caller]` is converted to a function pointer
|
||||
/// * If KCFI is enabled, creating a function pointer from a method on an object-safe trait.
|
||||
/// This includes the case of converting `::call`-like methods on closure-likes to function
|
||||
/// pointers.
|
||||
FnPtr,
|
||||
/// This `ReifyShim` was created to populate a vtable. Currently, this happens when a
|
||||
/// `#[track_caller]` mismatch occurs between the implementation of a method and the method.
|
||||
/// This includes the case of `::call`-like methods in closure-likes' vtables.
|
||||
Vtable,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)]
|
||||
pub enum InstanceDef<'tcx> {
|
||||
|
@ -67,7 +89,13 @@ pub enum InstanceDef<'tcx> {
|
|||
/// Because this is a required part of the function's ABI but can't be tracked
|
||||
/// as a property of the function pointer, we use a single "caller location"
|
||||
/// (the definition of the function itself).
|
||||
ReifyShim(DefId),
|
||||
///
|
||||
/// The second field encodes *why* this shim was created. This allows distinguishing between
|
||||
/// a `ReifyShim` that appears in a vtable vs one that appears as a function pointer.
|
||||
///
|
||||
/// This field will only be populated if we are compiling in a mode that needs these shims
|
||||
/// to be separable, currently only when KCFI is enabled.
|
||||
ReifyShim(DefId, Option<ReifyReason>),
|
||||
|
||||
/// `<fn() as FnTrait>::call_*` (generated `FnTrait` implementation for `fn()` pointers).
|
||||
///
|
||||
|
@ -194,7 +222,7 @@ impl<'tcx> InstanceDef<'tcx> {
|
|||
match self {
|
||||
InstanceDef::Item(def_id)
|
||||
| InstanceDef::VTableShim(def_id)
|
||||
| InstanceDef::ReifyShim(def_id)
|
||||
| InstanceDef::ReifyShim(def_id, _)
|
||||
| InstanceDef::FnPtrShim(def_id, _)
|
||||
| InstanceDef::Virtual(def_id, _)
|
||||
| InstanceDef::Intrinsic(def_id)
|
||||
|
@ -354,7 +382,9 @@ fn fmt_instance(
|
|||
match instance.def {
|
||||
InstanceDef::Item(_) => Ok(()),
|
||||
InstanceDef::VTableShim(_) => write!(f, " - shim(vtable)"),
|
||||
InstanceDef::ReifyShim(_) => write!(f, " - shim(reify)"),
|
||||
InstanceDef::ReifyShim(_, None) => write!(f, " - shim(reify)"),
|
||||
InstanceDef::ReifyShim(_, Some(ReifyReason::FnPtr)) => write!(f, " - shim(reify-fnptr)"),
|
||||
InstanceDef::ReifyShim(_, Some(ReifyReason::Vtable)) => write!(f, " - shim(reify-vtable)"),
|
||||
InstanceDef::ThreadLocalShim(_) => write!(f, " - shim(tls)"),
|
||||
InstanceDef::Intrinsic(_) => write!(f, " - intrinsic"),
|
||||
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{num}"),
|
||||
|
@ -476,15 +506,16 @@ impl<'tcx> Instance<'tcx> {
|
|||
debug!("resolve(def_id={:?}, args={:?})", def_id, args);
|
||||
// Use either `resolve_closure` or `resolve_for_vtable`
|
||||
assert!(!tcx.is_closure_like(def_id), "Called `resolve_for_fn_ptr` on closure: {def_id:?}");
|
||||
let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::FnPtr);
|
||||
Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| {
|
||||
match resolved.def {
|
||||
InstanceDef::Item(def) if resolved.def.requires_caller_location(tcx) => {
|
||||
debug!(" => fn pointer created for function with #[track_caller]");
|
||||
resolved.def = InstanceDef::ReifyShim(def);
|
||||
resolved.def = InstanceDef::ReifyShim(def, reason);
|
||||
}
|
||||
InstanceDef::Virtual(def_id, _) => {
|
||||
debug!(" => fn pointer created for virtual call");
|
||||
resolved.def = InstanceDef::ReifyShim(def_id);
|
||||
resolved.def = InstanceDef::ReifyShim(def_id, reason);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -508,6 +539,7 @@ impl<'tcx> Instance<'tcx> {
|
|||
debug!(" => associated item with unsizeable self: Self");
|
||||
Some(Instance { def: InstanceDef::VTableShim(def_id), args })
|
||||
} else {
|
||||
let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable);
|
||||
Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| {
|
||||
match resolved.def {
|
||||
InstanceDef::Item(def) => {
|
||||
|
@ -544,18 +576,18 @@ impl<'tcx> Instance<'tcx> {
|
|||
// Create a shim for the `FnOnce/FnMut/Fn` method we are calling
|
||||
// - unlike functions, invoking a closure always goes through a
|
||||
// trait.
|
||||
resolved = Instance { def: InstanceDef::ReifyShim(def_id), args };
|
||||
resolved = Instance { def: InstanceDef::ReifyShim(def_id, reason), args };
|
||||
} else {
|
||||
debug!(
|
||||
" => vtable fn pointer created for function with #[track_caller]: {:?}", def
|
||||
);
|
||||
resolved.def = InstanceDef::ReifyShim(def);
|
||||
resolved.def = InstanceDef::ReifyShim(def, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
InstanceDef::Virtual(def_id, _) => {
|
||||
debug!(" => vtable fn pointer created for virtual call");
|
||||
resolved.def = InstanceDef::ReifyShim(def_id);
|
||||
resolved.def = InstanceDef::ReifyShim(def_id, reason)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue