Skip to main content
Miri is an experimental interpreter for Rust’s Mid-level Intermediate Representation (MIR) that detects undefined behavior, memory errors, and concurrency issues in Rust programs.

Overview

Miri executes Rust code at the MIR level and checks for various forms of undefined behavior that could lead to bugs or security vulnerabilities. It’s an essential tool for testing unsafe code and ensuring soundness.
Miri does not catch every violation of the Rust specification. It uses its own approximation of undefined behavior and tests one of many possible executions of your program.

What Miri Detects

Miri can detect a wide range of undefined behaviors:

Memory Safety

  • Out-of-bounds memory accesses
  • Use-after-free
  • Invalid use of uninitialized data
  • Misaligned memory accesses

Type Safety

  • Invalid enum discriminants
  • Invalid bool values (not 0 or 1)
  • Violation of type invariants
  • Invalid references

Concurrency

  • Data races
  • Weak memory effects
  • Thread synchronization issues
  • Atomic operation violations

Intrinsic Safety

  • unreachable_unchecked being reached
  • copy_nonoverlapping with overlapping ranges
  • Precondition violations
  • Memory leaks

Experimental Features

  • Stacked Borrows: Aliasing rules for references
  • Tree Borrows: Alternative aliasing model

Installation

1

Install Rust nightly

Miri requires the nightly toolchain:
rustup toolchain install nightly
2

Add Miri component

rustup +nightly component add miri
3

Setup Miri

Perform initial setup (installs dependencies):
cargo +nightly miri setup
4

Run Miri

cargo +nightly miri test
cargo +nightly miri run

Basic Usage

Running Tests

# Run all tests with Miri
cargo miri test

# Run specific test
cargo miri test test_name

# Run tests matching a filter
cargo miri test filter

Running Binaries

# Run the main binary
cargo miri run

# Run a specific binary
cargo miri run --bin my_binary

# Pass arguments to the program
cargo miri run -- arg1 arg2

Cross-Interpretation

Run code for different target architectures:
# Run as Linux program (better support than Windows)
cargo miri run --target x86_64-unknown-linux-gnu

# Test on big-endian architecture
cargo miri test --target s390x-unknown-linux-gnu

# Test on different pointer width
cargo miri test --target i686-unknown-linux-gnu
Cross-interpretation is particularly useful for testing endian-sensitive code and platform-specific behavior without needing the actual hardware.

Configuration Flags

Miri provides extensive configuration through -Z flags set via MIRIFLAGS:

Common Flags

# Disable isolation (access host environment)
MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test

# Ignore memory leaks
MIRIFLAGS="-Zmiri-ignore-leaks" cargo miri run

# Use a specific seed for determinism
MIRIFLAGS="-Zmiri-seed=42" cargo miri test

# Test multiple seeds
MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test

# Make concurrency fully deterministic
MIRIFLAGS="-Zmiri-deterministic-concurrency" cargo miri test

Environment Variable Forwarding

# Forward specific environment variable
MIRIFLAGS="-Zmiri-env-forward=MY_VAR" cargo miri test

# Set environment variable for the program
MIRIFLAGS="-Zmiri-env-set=MY_VAR=value" cargo miri run

Backtrace Configuration

# Get backtraces (requires disabling isolation)
RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test

# Configure backtrace verbosity
MIRIFLAGS="-Zmiri-backtrace=full" cargo miri run

Testing Multiple Executions

Miri can explore different execution paths:
# Test with 64 different seeds (default)
MIRIFLAGS="-Zmiri-many-seeds" cargo miri test

# Test with specific range of seeds
MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo miri test

# Continue even after finding failures
MIRIFLAGS="-Zmiri-many-seeds-keep-going" cargo miri test
Different seeds can reveal bugs that only occur under specific thread interleavings or memory layouts.

Building Miri from Source

Miri is located at src/tools/miri/ in the Rust repository.

Package Structure

From src/tools/miri/Cargo.toml:
[package]
name = "miri"
version = "0.1.0"
edition = "2024"
description = "An experimental interpreter for Rust MIR (core driver)."

[dependencies]
getrandom = { version = "0.3", features = ["std"] }
rand = "0.9"
smallvec = { version = "1.7", features = ["drain_filter"] }
aes = { version = "0.8.3", features = ["hazmat"] }
measureme = "12"
chrono = { version = "0.4.38", default-features = false }

[features]
default = ["stack-cache", "native-lib"]
genmc = ["dep:genmc-sys"]
stack-cache = []
expensive-consistency-checks = ["stack-cache"]
tracing = ["serde_json"]
native-lib = ["dep:libffi", "dep:libloading", ...]

Build with Bootstrap

# Build Miri
./x build miri

# Test Miri
./x test miri

# Build and install Miri
./x install miri

Miri Components

Miri consists of several components:
  • miri: Core interpreter (main crate)
  • cargo-miri: Cargo integration tool
  • miri-script: Development and testing scripts
  • test-cargo-miri: Integration tests
  • bench-cargo-miri: Benchmarks under Miri

Ignoring Tests Under Miri

Some tests may not work with Miri:
#[test]
#[cfg_attr(miri, ignore)]
fn test_requires_native_io() {
    // This test uses features Miri doesn't support
    std::fs::read("file.txt").unwrap();
}

// Or check at runtime
#[test]
fn conditional_test() {
    if cfg!(miri) {
        // Simplified version for Miri
    } else {
        // Full native test
    }
}

Parallel Testing

Miri is single-threaded, but you can parallelize test execution:
# Install cargo-nextest
cargo install cargo-nextest

# Run tests in parallel
cargo miri nextest run -j8
cargo-nextest runs each test in a separate process, so it won’t detect data races between tests that share resources.

CI Integration

GitHub Actions

miri:
  name: "Miri"
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - name: Install Miri
      run: |
        rustup toolchain install nightly --component miri
        rustup override set nightly
        cargo miri setup
    - name: Test with Miri
      run: cargo miri test

Running on Different Targets

- name: Test with Miri on multiple targets
  run: |
    cargo miri test --target x86_64-unknown-linux-gnu
    cargo miri test --target s390x-unknown-linux-gnu

Supported Targets

Miri supports various targets with different levels of support:
  • Tier 1: All Rust Tier 1 targets fully supported
  • Big-endian: s390x-unknown-linux-gnu as the primary big-endian test target
  • Linux/macOS/Windows: Generally work, with Linux having the best support
  • Solaris/illumos: Unofficial support (maintained by @devnexen)
  • FreeBSD: Unofficial support (maintained by @YohDeadfall and @LorrensP-2158466)
  • Android: Unofficial support (maintainer wanted)
When testing on Windows, consider using --target x86_64-unknown-linux-gnu for better API support.

Advanced Configuration

Aliasing Models

# Disable Stacked Borrows
MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri test

# Use Tree Borrows instead
MIRIFLAGS="-Zmiri-tree-borrows" cargo miri test

# Track pointer tags for debugging
MIRIFLAGS="-Zmiri-track-pointer-tag=123" cargo miri run

Memory and Performance

# Track specific allocations
MIRIFLAGS="-Zmiri-track-alloc-id=42,43" cargo miri test

# Control preemption rate
MIRIFLAGS="-Zmiri-preemption-rate=0.1" cargo miri test

# Report progress for long-running programs
MIRIFLAGS="-Zmiri-report-progress" cargo miri run

Numeric Precision

# Make floating-point fully deterministic
MIRIFLAGS="-Zmiri-deterministic-floats" cargo miri test

# Disable extra rounding errors
MIRIFLAGS="-Zmiri-no-extra-rounding-error" cargo miri test

Known Limitations

Miri has several important limitations:
  • Platform-independent interpreter - limited FFI and platform API support
  • Tests one execution path - may miss bugs in other paths
  • Non-deterministic behavior - results may vary with different seeds
  • Performance - much slower than native execution
  • Not all system APIs supported - especially networking

Real-World Bugs Found

Miri has discovered numerous bugs in production code:
  • Memory leaks in Vec and BTreeMap
  • Unaligned accesses in rand and encoding_rs
  • Data races in arc-swap and thread::scope
  • Use-after-free in servo_arc
  • Invalid pointer arithmetic in TiKV
See the full list in the Miri README.

Resources

  • GitHub Repository: github.com/rust-lang/miri
  • Zulip Channel: Miri stream
  • Contributing Guide: CONTRIBUTING.md in the Miri repository
  • Source Code: src/tools/miri/ in the Rust repository
  • Research Papers: Multiple academic papers on Stacked Borrows, Tree Borrows, and Miri itself

Build docs developers (and LLMs) love