Unsafe Rust

Soundness and Safety Invariants

Soundness is the property that no safe Rust code can ever cause undefined behavior when interacting with a type, function, or module through its public safe API—regardless of how adversarial, unusual, or panicky that safe caller is. If any sequence of safe function calls can trigger undefined behavior inside a library's internal unsafe blocks, that library is unsound.

Validity Invariants vs. Safety Invariants

Rust distinguishes two levels of invariants on types:

+-----------------------------------------------------------------------+
|  All Bit Patterns of Size N                                           |
|  +-----------------------------------------------------------------+  |
|  |  Validity Invariant (Compiler / Language Requirement)           |  |
|  |  - Violating this at ANY instant is immediate UB                |  |
|  |  - e.g. `&[u8]` inside `str` must be non-null & aligned         |  |
|  |  +-----------------------------------------------------------+  |  |
|  |  |  Safety Invariant (Library / Type Contract)               |  |  |
|  |  |  - Unsafe code in the library relies on this holding      |  |  |
|  |  |  - e.g. bytes inside `str` / `String` must be valid UTF-8 |  |  |
|  |  +-----------------------------------------------------------+  |  |
|  +-----------------------------------------------------------------+  |
+-----------------------------------------------------------------------+
Invariant Kind Enforced By Consequence of Violation Example (String)
Validity Invariant Language / LLVM abstract machine Immediate UB the moment the invalid value is constructed The internal NonNull<u8> pointer is non-null, and len / cap are initialized usize integers
Safety Invariant Encapsulating module's API boundary Soundness bug (causes UB later when another unsafe block trusts the invariant) ptr points to a heap allocation of capacity cap with 0..len initialized as valid UTF-8 bytes

How Safe Code Can Break a Broken Safety Invariant (Unsoundness Example)

Suppose a custom AsciiString type relies on the safety invariant that its internal Vec<u8> contains only ASCII bytes (0x00..=0x7F), and uses an unsafe block in .as_str() calling str::from_utf8_unchecked(&self.bytes).

If AsciiString accidentally makes pub bytes: Vec<u8> public (or implements DerefMut<Target = Vec<u8>>), safe callers can push 0xFF into bytes without writing unsafe, and then call .as_str() to trigger undefined behavior!

pub struct AsciiString {
    // Private field! External safe code cannot push non-ASCII bytes.
    // Safety invariant: every byte in `bytes` satisfies `b <= 0x7F`.
    bytes: Vec<u8>,
}

impl AsciiString {
    pub fn try_new(input: Vec<u8>) -> Result<Self, Vec<u8>> {
        if input.is_ascii() {
            Ok(Self { bytes: input })
        } else {
            Err(input)
        }
    }

    pub fn push_ascii(&mut self, byte: u8) -> bool {
        if byte.is_ascii() {
            self.bytes.push(byte);
            true
        } else {
            false
        }
    }

    pub fn as_str(&self) -> &str {
        // SAFETY: Our module invariant guarantees that `self.bytes` only ever
        // contains valid ASCII (`0x00..=0x7F`), and all ASCII is valid UTF-8.
        unsafe { std::str::from_utf8_unchecked(&self.bytes) }
    }
}

fn main() {
    let mut s = AsciiString::try_new(b"Rust".to_vec()).unwrap();
    assert!(s.push_ascii(b'!'));
    assert!(!s.push_ascii(0xFF)); // Rejected; invariant preserved!

    println!("sound AsciiString: {:?}", s.as_str());
}
sound AsciiString: "Rust!"

Never Trust Safe Traits for Soundness (Ord, Eq, Hash, Deref)

An unsafe block must never rely on a safe trait implementation (Ord, PartialEq, Hash, Clone, or Deref) returning consistent results for memory safety! Because any safe user can implement Ord with a buggy comparator that returns Ordering::Less on the first call and Ordering::Greater on the second call for the same inputs, an unsafe sorting algorithm that skips bounds checks assuming Ord is a strict total order is unsound. Only unsafe traits can be trusted for memory safety.