Document that CFI diverges from Rust wrt. ABI-compatibility rules

This commit is contained in:
Alice Ryhl
2026-04-28 15:16:49 +00:00
parent 9620eae30a
commit aef93ca43a
2 changed files with 35 additions and 1 deletions
+4 -1
View File
@@ -1794,6 +1794,7 @@ mod prim_ref {}
/// have different sizes.
///
/// ### ABI compatibility
/// [ABI compatibility]: #abi-compatibility
///
/// Generally, when a function is declared with one signature and called via a function pointer with
/// a different signature, the two signatures must be *ABI-compatible* or else calling the function
@@ -1831,7 +1832,7 @@ mod prim_ref {}
/// - `*const T`, `*mut T`, `&T`, `&mut T`, `Box<T>` (specifically, only `Box<T, Global>`), and
/// `NonNull<T>` are all ABI-compatible with each other for all `T`. They are also ABI-compatible
/// with each other for _different_ `T` if they have the same metadata type (`<T as
/// Pointee>::Metadata`).
/// Pointee>::Metadata`). However, see the [Control Flow Integrity][cfi-docs] docs for caveats.
/// - `usize` is ABI-compatible with the `uN` integer type of the same size, and likewise `isize` is
/// ABI-compatible with the `iN` integer type of the same size.
/// - `char` is ABI-compatible with `u32`.
@@ -1890,6 +1891,8 @@ mod prim_ref {}
/// Behavior since transmuting `None::<NonZero<i32>>` to `NonZero<i32>` violates the non-zero
/// requirement.
///
/// [cfi-docs]: https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html#controlflowintegrity
///
/// ### Trait implementations
///
/// In this documentation the shorthand `fn(T₁, T₂, …, Tₙ)` is used to represent non-variadic
@@ -246,6 +246,37 @@ Cargo build-std feature (i.e., `-Zbuild-std`) when enabling CFI.
See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
## Divergence from the Rust ABI
There are some caveats to [the ABI-compatibility rules for Rust-to-Rust
calls][rust-abi] due to how the CFI sanitizer is implemented. CFI is a tool
that can be used to validate that dynamic function calls respect the ABI, but
due to its C/C++ origins, it disagrees with the above documented guarantees in
a few ways, see below. As CFI is unstable, the details may change in the
future.
When running the CFI sanitizer, pointer types are only ABI-compatible if the
target type and mutability is the same. This means that `*mut String` and `*mut
i32` are incompatible when using CFI. It also means that `*mut i32` is
incompatible with `*const i32`. The `NonNull<_>` and `Box<_>` pointer types are
currently considered immutable under CFI. For non-primitive target types, CFI
uses the name of the type for its compatibility check.
When not using the `-Zsanitizer-cfi-normalize-integers` flag, the CFI sanitizer
further restricts the rules by considering `usize`/`isize` incompatible with
the `uN`/`iN` integer type of the same size.
Unlike other cases where the function ABI is violated, function calls that
violate the CFI-specific ABI-compatibility rules are not undefined behavior.
They are guaranteed to either function correctly, or to crash the program.
This section only covers cases where CFI disagrees with the ABI-compatibility
rules for Rust-to-Rust calls. It is not meant to be a complete explanation of
how CFI works, and details important for C-to-Rust or Rust-to-C calls are
omitted.
[rust-abi]: https://doc.rust-lang.org/stable/std/primitive.fn.html#abi-compatibility
## Example 1: Redirecting control flow using an indirect branch/call to an invalid destination
```rust