diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 3a4e1e657a3d..48bf57356b57 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -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` (specifically, only `Box`), and /// `NonNull` 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 (`::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::>` to `NonZero` 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 diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md index b0f6c97ff5a7..5bd518b14154 100644 --- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md +++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md @@ -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