Open-source project
rust-lang/miri avatar
rust-lang/miri

Miri: running Rust test suites through an interpreter to catch undefined behavior

An interpreter for Rust's mid-level intermediate representation

6,626 stars532 forksRustApache-2.0

At a glance

What is it?
Miri is an Undefined Behavior detection tool for Rust that executes cargo test suites inside an interpreter, and it is the reason unsafe blocks get exercised rather than merely compiled. This review covers how it works, how to install it on nightly, what it cannot see, and when loom or Kani is the better fit.
Who is it for?
Adopt Miri if you maintain unsafe code, a data structure, or an FFI-free crate and want your existing test suite to also check for undefined behavior; skip it if your crate is pure safe Rust with no layout assumptions, or if you need network access under test.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap Miri fills between rustc and a real run

The Rust compiler checks types and borrows, but it does not execute your program. A test binary that passes under cargo test proves that the paths it took produced the expected values, not that the pointers along the way were valid. Miri is an interpreter for Rust's mid-level intermediate representation, and it runs the program instruction by instruction while tracking the metadata that a compiled binary throws away: which allocations are still live, which bytes were initialized, which references are currently allowed to be used for writes.

The audience is narrow and specific. If your crate contains no unsafe blocks and no dependencies that do, Miri has little to examine, though it will still catch leaks and some library-level misuse. The tool earns its place in crates that implement collections, allocators, serialization formats, lock-free structures, or anything that reasons about layout and aliasing. The README frames the scope plainly: Miri detects unsafe code that fails to uphold its safety requirements, listing out-of-bounds memory accesses, use-after-free, invalid use of uninitialized data, alignment violations, and invalid enum discriminants among the categories it reports.

One design decision deserves attention early. Miri is not a soundness prover. The README states that when Miri finds UB your code is definitely unsound, but when Miri does not find UB you may simply need to test more inputs or more non-deterministic choices. That asymmetry is the whole mental model: a clean Miri run is evidence, not a certificate.

How the interpreter tracks allocations, initialization and aliasing

Miri does not compile your code to machine instructions and run it. It interprets MIR, the representation rustc builds after type checking and before code generation, which is why it must track the toolchain version so closely. Every allocation the program makes is represented explicitly, with a record of whether each byte has been written and whether the allocation has been freed. Reading a byte that was never initialized, or reading through a pointer whose allocation is gone, becomes a detectable event rather than whatever the host memory happened to contain.

On top of that bookkeeping sit the aliasing models. The README describes Stacked Borrows as experimental, governing aliasing for reference types, and Tree Borrows as an optional alternative. Both attempt to answer a question the compiler cannot check at compile time: is this sequence of reference creations and uses legal under Rust's aliasing rules? Because the models are still being developed, a violation report is a strong signal but the rules themselves are a moving target.

Determinism is enforced by default. Miri isolates the program from the host, replacing entropy sources, environment variables and clocks with fake implementations so that a given run is reproducible. The README is explicit that this isolation is not a sandbox and that gaps in it are treated as ordinary bugs rather than security bugs. It also warns that the fake RNG makes Miri unsuitable for cryptographic use, so keys must never be generated under it. When a test genuinely needs the host, MIRIFLAGS="-Zmiri-disable-isolation" switches to the real system APIs.

Installing Miri and running a first test suite

Miri ships as a rustup component and is installed on the nightly toolchain, not stable. The README gives a single command for the installation:

bash
rustup +nightly component add miri

After that, the project needs to be pinned to nightly, either once for the directory or per command. The README shows the override form and notes that cargo +nightly works as an alternative for each invocation:

bash
rustup override set nightly

The first run performs extra setup and installs dependencies, and the README states that Miri asks for confirmation before installing anything. Expect that first invocation to be slower than later ones.

Running the test suite is then a drop-in replacement for cargo test:

bash
cargo miri test

Flags pass through unchanged, so filtering works the way it does under cargo. The README gives this example directly:

bash
cargo miri test filter

For a binary crate rather than a library, the equivalent entry point is cargo miri run. Miri also supports cross-interpretation, which lets a program be executed for a different target than the host. The README recommends --target x86_64-unknown-linux-gnu on Windows because system API support varies between targets and Linux targets have the better coverage.

What Miri cannot see, and where it becomes the wrong tool

The largest limitation is non-determinism. The README states that execution is non-deterministic whenever it depends on where allocations land in memory or on the exact interleaving of concurrent threads, and that Miri tests one of many possible executions. A bug that only appears under a different interleaving will not be reported. Running with different values of -Zmiri-seed widens the sample somewhat, but the README is candid that this comes nowhere near exploring all possible executions.

The second limitation is the platform boundary. Miri runs the program as a platform-independent interpreter, so most platform-specific APIs and FFI are unavailable. Printing to stdout, environment variables and basic file system access are implemented; networking is not, according to the README. Any crate whose tests open sockets, bind ports, or call into a C library will need those paths excluded or mocked, and the exclusion is where the risk moves: the untested FFI boundary is exactly the part Miri cannot inspect.

Weak memory emulation is incomplete as well. The README notes that there are legal behaviors Miri will never produce, while also producing behaviors that are hard to observe on real hardware, which makes it useful for finding weak memory concurrency bugs but not decisive. For complicated atomic code the README itself points to specialized tools such as loom. Finally, layout-dependent code can pass under Miri and still break on a different compiler version or platform, because unspecified layout details are not checked unless -Zrandomize-layout is enabled.

Miri against loom and Kani: three different bets

The README names loom directly as the tool to use when you need real confidence in complicated atomic code. The difference is one of method rather than degree. Miri interprets a single execution of your actual test binary and observes what that execution does; loom instead takes a concurrency test written against its own synchronization primitives and systematically permutes thread interleavings, which is why it can cover schedules Miri will never happen to hit. Loom requires rewriting the code under test to use its types, so it fits libraries that can be generic over the synchronization layer. Miri requires no rewrite at all.

Kani is a different bet again, appearing in the related searches around this project. Where Miri runs your tests, a bounded model checker explores paths against a property you state, which can reach inputs no test suite enumerates. That power comes with proof obligations and a harness to write. The practical split is that Miri reuses the tests you already have and reports concrete UB when it reaches it, loom targets the specific problem of thread interleavings, and a model checker targets input coverage. None of the three subsumes the others, and a crate with unsafe pointer arithmetic and a lock-free queue may reasonably want Miri for the former and loom for the latter.

Maintenance cost, toolchain pinning and the licence split

Miri tracks the compiler rather than a stable release cadence. The README says it will be updated with the Rust compiler to protect against UB as understood by the current compiler, and that it makes no promises about future versions of rustc. In practice this means Miri is tied to nightly, and a pinned nightly that drifts too far from the Miri component can force an upgrade of both. The repository itself was last pushed on 2026-09-21 and is not archived, so the component is current, but there are no tagged releases listed, which makes the rustup component the only supported installation path.

Upgrading Miri is therefore an upgrade of the toolchain, and the failure mode to watch is a new aliasing rule turning previously clean code into a report. That is the tool working as intended, but it lands on your schedule rather than one you chose.

Licensing is split. The repository carries both LICENSE-APACHE and LICENSE-MIT, while Cargo.toml declares the package as MIT OR Apache-2.0. That dual arrangement is the Rust ecosystem norm and permits use under either licence, but the choice of which terms apply to a redistribution is a decision for your own counsel, not something this article can settle.

Editorial conclusion

Adopt Miri if you maintain unsafe code, a data structure, or an FFI-free crate and want your existing test suite to also check for undefined behavior; skip it if your crate is pure safe Rust with no layout assumptions, or if you need network access under test. Before relying on it, confirm that your tests actually exercise the unsafe paths, because Miri only reports UB that a particular execution reaches, and check whether the isolation defaults block any host API your tests call.

Frequently asked questions

Does Miri work on stable Rust?

No. The README installs it with rustup +nightly component add miri, and all subsequent commands assume the nightly toolchain is pinned or that cargo +nightly is used per command.

What kinds of bugs does Miri detect?

The README lists out-of-bounds memory accesses, use-after-free, invalid use of uninitialized data, violated intrinsic preconditions, misaligned accesses, invalid type invariants such as a bool that is not 0 or 1, and data races. It also reports memory leaks when memory is still allocated at the end of execution and is not reachable from a global static.

Can Miri prove that my unsafe code is sound?

No. The README states that Miri cannot ensure soundness: it can only tell you whether a particular way of interacting with your code causes undefined behavior in a particular execution. Finding UB proves unsoundness, but finding none may just mean more inputs or interleavings need testing.

How do I run my tests under Miri?

Use cargo miri test for a library or test suite, or cargo miri run for a binary project. The README notes that these accept the same flags as cargo test and cargo run, so cargo miri test filter runs only tests whose names contain filter.

Can Miri be used to generate cryptographic keys?

No. The README warns that the fake system RNG APIs make Miri not suited for cryptographic use, and says explicitly not to generate keys using Miri.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. rust-lang/miri on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rust-lang-miri.svg)](https://hysenlabs.com/projects/rust-lang-miri)