@@ -31,6 +31,28 @@ pub struct Instance<'tcx> {
3131 pub args : GenericArgsRef < ' tcx > ,
3232}
3333
34+ /// Describes why a `ReifyShim` was created. This is needed to distingish a ReifyShim created to
35+ /// adjust for things like `#[track_caller]` in a vtable from a `ReifyShim` created to produce a
36+ /// function pointer from a vtable entry.
37+ /// Currently, this is only used when KCFI is enabled, as only KCFI needs to treat those two
38+ /// `ReifyShim`s differently.
39+ #[ derive( Copy , Clone , PartialEq , Eq , Hash , Debug ) ]
40+ #[ derive( TyEncodable , TyDecodable , HashStable ) ]
41+ pub enum ReifyReason {
42+ /// The `ReifyShim` was created to produce a function pointer. This happens when:
43+ /// * A vtable entry is directly converted to a function call (e.g. creating a fn ptr from a
44+ /// method on a `dyn` object).
45+ /// * A function with `#[track_caller]` is converted to a function pointer
46+ /// * If KCFI is enabled, creating a function pointer from a method on an object-safe trait.
47+ /// This includes the case of converting `::call`-like methods on closure-likes to function
48+ /// pointers.
49+ FnPtr ,
50+ /// This `ReifyShim` was created to populate a vtable. Currently, this happens when a
51+ /// `#[track_caller]` mismatch occurs between the implementation of a method and the method.
52+ /// This includes the case of `::call`-like methods in closure-likes' vtables.
53+ Vtable ,
54+ }
55+
3456#[ derive( Copy , Clone , PartialEq , Eq , Hash , Debug ) ]
3557#[ derive( TyEncodable , TyDecodable , HashStable , TypeFoldable , TypeVisitable , Lift ) ]
3658pub enum InstanceDef < ' tcx > {
@@ -67,7 +89,13 @@ pub enum InstanceDef<'tcx> {
6789 /// Because this is a required part of the function's ABI but can't be tracked
6890 /// as a property of the function pointer, we use a single "caller location"
6991 /// (the definition of the function itself).
70- ReifyShim ( DefId ) ,
92+ ///
93+ /// The second field encodes *why* this shim was created. This allows distinguishing between
94+ /// a `ReifyShim` that appears in a vtable vs one that appears as a function pointer.
95+ ///
96+ /// This field will only be populated if we are compiling in a mode that needs these shims
97+ /// to be separable, currently only when KCFI is enabled.
98+ ReifyShim ( DefId , Option < ReifyReason > ) ,
7199
72100 /// `<fn() as FnTrait>::call_*` (generated `FnTrait` implementation for `fn()` pointers).
73101 ///
@@ -194,7 +222,7 @@ impl<'tcx> InstanceDef<'tcx> {
194222 match self {
195223 InstanceDef :: Item ( def_id)
196224 | InstanceDef :: VTableShim ( def_id)
197- | InstanceDef :: ReifyShim ( def_id)
225+ | InstanceDef :: ReifyShim ( def_id, _ )
198226 | InstanceDef :: FnPtrShim ( def_id, _)
199227 | InstanceDef :: Virtual ( def_id, _)
200228 | InstanceDef :: Intrinsic ( def_id)
@@ -354,7 +382,9 @@ fn fmt_instance(
354382 match instance. def {
355383 InstanceDef :: Item ( _) => Ok ( ( ) ) ,
356384 InstanceDef :: VTableShim ( _) => write ! ( f, " - shim(vtable)" ) ,
357- InstanceDef :: ReifyShim ( _) => write ! ( f, " - shim(reify)" ) ,
385+ InstanceDef :: ReifyShim ( _, None ) => write ! ( f, " - shim(reify)" ) ,
386+ InstanceDef :: ReifyShim ( _, Some ( ReifyReason :: FnPtr ) ) => write ! ( f, " - shim(reify-fnptr)" ) ,
387+ InstanceDef :: ReifyShim ( _, Some ( ReifyReason :: Vtable ) ) => write ! ( f, " - shim(reify-vtable)" ) ,
358388 InstanceDef :: ThreadLocalShim ( _) => write ! ( f, " - shim(tls)" ) ,
359389 InstanceDef :: Intrinsic ( _) => write ! ( f, " - intrinsic" ) ,
360390 InstanceDef :: Virtual ( _, num) => write ! ( f, " - virtual#{num}" ) ,
@@ -476,15 +506,34 @@ impl<'tcx> Instance<'tcx> {
476506 debug ! ( "resolve(def_id={:?}, args={:?})" , def_id, args) ;
477507 // Use either `resolve_closure` or `resolve_for_vtable`
478508 assert ! ( !tcx. is_closure_like( def_id) , "Called `resolve_for_fn_ptr` on closure: {def_id:?}" ) ;
509+ let reason = tcx. sess . is_sanitizer_kcfi_enabled ( ) . then_some ( ReifyReason :: FnPtr ) ;
479510 Instance :: resolve ( tcx, param_env, def_id, args) . ok ( ) . flatten ( ) . map ( |mut resolved| {
480511 match resolved. def {
481512 InstanceDef :: Item ( def) if resolved. def . requires_caller_location ( tcx) => {
482513 debug ! ( " => fn pointer created for function with #[track_caller]" ) ;
483- resolved. def = InstanceDef :: ReifyShim ( def) ;
514+ resolved. def = InstanceDef :: ReifyShim ( def, reason ) ;
484515 }
485516 InstanceDef :: Virtual ( def_id, _) => {
486517 debug ! ( " => fn pointer created for virtual call" ) ;
487- resolved. def = InstanceDef :: ReifyShim ( def_id) ;
518+ resolved. def = InstanceDef :: ReifyShim ( def_id, reason) ;
519+ }
520+ // Reify `Trait::method` implementations if KCFI is enabled
521+ // FIXME(maurer) only reify it if it is a vtable-safe function
522+ _ if tcx. sess . is_sanitizer_kcfi_enabled ( )
523+ && tcx. associated_item ( def_id) . trait_item_def_id . is_some ( ) =>
524+ {
525+ // If this function could also go in a vtable, we need to `ReifyShim` it with
526+ // KCFI because it can only attach one type per function.
527+ resolved. def = InstanceDef :: ReifyShim ( resolved. def_id ( ) , reason)
528+ }
529+ // Reify `::call`-like method implementations if KCFI is enabled
530+ _ if tcx. sess . is_sanitizer_kcfi_enabled ( )
531+ && tcx. is_closure_like ( resolved. def_id ( ) ) =>
532+ {
533+ // Reroute through a reify via the *unresolved* instance. The resolved one can't
534+ // be directly reified because it's closure-like. The reify can handle the
535+ // unresolved instance.
536+ resolved = Instance { def : InstanceDef :: ReifyShim ( def_id, reason) , args }
488537 }
489538 _ => { }
490539 }
@@ -508,6 +557,7 @@ impl<'tcx> Instance<'tcx> {
508557 debug ! ( " => associated item with unsizeable self: Self" ) ;
509558 Some ( Instance { def : InstanceDef :: VTableShim ( def_id) , args } )
510559 } else {
560+ let reason = tcx. sess . is_sanitizer_kcfi_enabled ( ) . then_some ( ReifyReason :: Vtable ) ;
511561 Instance :: resolve ( tcx, param_env, def_id, args) . ok ( ) . flatten ( ) . map ( |mut resolved| {
512562 match resolved. def {
513563 InstanceDef :: Item ( def) => {
@@ -544,18 +594,18 @@ impl<'tcx> Instance<'tcx> {
544594 // Create a shim for the `FnOnce/FnMut/Fn` method we are calling
545595 // - unlike functions, invoking a closure always goes through a
546596 // trait.
547- resolved = Instance { def : InstanceDef :: ReifyShim ( def_id) , args } ;
597+ resolved = Instance { def : InstanceDef :: ReifyShim ( def_id, reason ) , args } ;
548598 } else {
549599 debug ! (
550600 " => vtable fn pointer created for function with #[track_caller]: {:?}" , def
551601 ) ;
552- resolved. def = InstanceDef :: ReifyShim ( def) ;
602+ resolved. def = InstanceDef :: ReifyShim ( def, reason ) ;
553603 }
554604 }
555605 }
556606 InstanceDef :: Virtual ( def_id, _) => {
557607 debug ! ( " => vtable fn pointer created for virtual call" ) ;
558- resolved. def = InstanceDef :: ReifyShim ( def_id) ;
608+ resolved. def = InstanceDef :: ReifyShim ( def_id, reason )
559609 }
560610 _ => { }
561611 }
0 commit comments