@@ -72,6 +72,25 @@ macro_rules! argument_new {
72
72
// a `fn(&T, ...)`, so the invariant is maintained.
73
73
ty: ArgumentType :: Placeholder {
74
74
value: NonNull :: <$t>:: from_ref( $x) . cast( ) ,
75
+ // The Rust ABI considers all pointers to be equivalent, so transmuting a fn(&T) to
76
+ // fn(NonNull<()>) and calling it with a NonNull<()> that points at a T is allowed.
77
+ // However, the CFI sanitizer does not allow this, and triggers a crash when it
78
+ // happens.
79
+ //
80
+ // To avoid this crash, we use a helper function when CFI is enabled. To avoid the
81
+ // cost of this helper function (mainly code-size) when it is not needed, we
82
+ // transmute the function pointer otherwise.
83
+ //
84
+ // This is similar to what the Rust compiler does internally with vtables when KCFI
85
+ // is enabled, where it generates trampoline functions that only serve to adjust the
86
+ // expected type of the argument. `ArgumentType::Placeholder` is a bit like a
87
+ // manually constructed trait object, so it is not surprising that the same approach
88
+ // has to be applied here as well.
89
+ //
90
+ // It is still considered problematic (from the Rust side) that CFI rejects entirely
91
+ // legal Rust programs, so we do not consider anything done here a stable guarantee,
92
+ // but meanwhile we carry this work-around to keep Rust compatible with CFI and
93
+ // KCFI.
75
94
#[ cfg( not( any( sanitize = "cfi" , sanitize = "kcfi" ) ) ) ]
76
95
formatter: {
77
96
let f: fn ( & $t, & mut Formatter <' _>) -> Result = $f;
0 commit comments