25.9 Verifying Unsafe Code: Miri

Since the compiler’s guarantees do not extend into unsafe blocks, verifying the correctness of unsafe code is crucial. Miri is an experimental interpreter for Rust’s Mid-level Intermediate Representation (MIR). It executes Rust code (including unsafe blocks) and dynamically checks for certain types of undefined behavior.

Miri can detect violations such as:

  • Memory leaks (if enabled).
  • Out-of-bounds memory access (pointers and slices).
  • Use of uninitialized memory.
  • Use-after-free (accessing deallocated memory).
  • Invalid pointer alignment.
  • Violations of pointer aliasing rules (Stacked Borrows/Tree Borrows).
  • Invalid values for types with specific constraints (e.g., using a value other than 0 or 1 for bool, invalid enum discriminants).
  • Invalid transmute operations.
  • Data races (Miri has limited data race detection capabilities, primarily in single-threaded contexts by checking memory model violations).

25.9.1 Using Miri

Miri can be installed as a rustup component and run via Cargo:

  1. Install Miri:
    rustup component add miri
    
  2. Run Miri on your project’s tests:
    cargo miri test
    
  3. Run Miri on a specific binary target:
    cargo miri run --bin your_binary_name
    

If Miri encounters undefined behavior during execution, it will terminate the program and report an error detailing the violation type and location.

25.9.2 Example: Dangling Pointer Detection

Consider code that incorrectly returns a pointer to a stack variable:

fn create_dangling_pointer() -> *const i32 {
    let local_var = 100;
    let ptr = &local_var as *const i32;
    ptr // Return pointer to `local_var`
} // `local_var` goes out of scope here; its stack memory is now invalid.

fn main() {
    let dangling_ptr = create_dangling_pointer();

    // Unsafe: Dereferencing a dangling pointer is Undefined Behavior!
    unsafe {
        // Miri will likely detect this access as invalid.
        // Normal compilation might crash, print garbage, or appear to work by chance.
        println!("Attempting to read dangling pointer: {}", *dangling_ptr);
    }
}

Running this code using cargo miri run should trigger a Miri error report upon reaching the *dangling_ptr dereference, indicating an access to invalid memory (specifically, memory previously allocated on the stack frame of create_dangling_pointer, which no longer exists). Miri helps catch such errors that might otherwise go unnoticed in standard testing.