@@ -72,6 +72,25 @@ macro_rules! argument_new {
7272 // a `fn(&T, ...)`, so the invariant is maintained.
7373 ty: ArgumentType :: Placeholder {
7474 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.
7594 #[ cfg( not( any( sanitize = "cfi" , sanitize = "kcfi" ) ) ) ]
7695 formatter: {
7796 let f: fn ( & $t, & mut Formatter <' _>) -> Result = $f;
0 commit comments