Miri interprets MIR and turns undefined behavior into errors
An interpreter for Rust's mid-level intermediate representation
At a glance
- What is it?
- Miri is the undefined behavior detector in the Rust toolchain, and it works by interpreting MIR instead of running machine code. That buys determinism and precise diagnostics at the cost of networking, most FFI, and any claim about soundness.
- Who is it for?
- Run Miri in CI if you maintain unsafe code, because cargo miri test turns pointer mistakes, leaks and aliasing violations into build failures on the test suite you already have. Do not reach for it to certify soundness, to test networked or FFI-heavy code, or to generate keys, since the fake randomness and the missing system APIs rule all three out.
- 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 last received commits 1 day ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- rust-lang/miri6,746 ★ · +134
| Repository | 2026-09-23 | 2026-11-13 |
|---|---|---|
| rust-lang/miri | 6.6k | 6.7k |
134 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Miri interprets MIR, so the CPU never gets a vote
Miri does not run your program on the processor. It reads the mid-level intermediate representation that rustc produces and interprets it, which is why the crate description calls itself an experimental interpreter for Rust MIR. The consequences follow from that single decision.
What you get in exchange is a hard error where an ordinary run would give you a wrong answer. Out-of-bounds accesses and use-after-free stop the program. Using uninitialized data stops the program. So does a bool that is not 0 or 1, an invalid enum discriminant, reaching an unreachable_unchecked intrinsic, calling copy_nonoverlapping with overlapping ranges, or a memory access whose alignment is too small.
The unit of work is a cargo project rather than a function. Miri runs the test suite, or a binary, and reports the point where the program left the rules, which is why it ships as a toolchain component and is driven through cargo.
Data races, bad discriminants and overlapping copies all become errors
The aliasing checks are the part labelled experimental, and they are where the configuration surface begins. Two models are available: Stacked Borrows, which governs aliasing for reference types, and Tree Borrows, offered as an alternative to it. Both appear in the project's own list of what gets detected, and both are marked experimental. Stacked Borrows can also be switched off for a single run with MIRIFLAGS="-Zmiri-disable-stacked-borrows", which executes the program without checking reference aliasing at all.
Miri reports leaks as well. When memory is still allocated at the end of an execution and that memory is not reachable from a global static, it raises an error instead of returning quietly. The static exemption exists so that allocations a program intends to keep for the life of the process do not read as failures.
Concurrency is checked, within a stated boundary. Data races are detected, and some weak memory effects are emulated, so an atomic read can return an outdated value. The emulation is incomplete in one direction: there are legal behaviors Miri will never produce. It is generous in the other, producing many behaviors that are hard to observe on real hardware, which is the whole point for a race that shows up once in a thousand runs.
Isolation fakes the clock, the entropy and the environment
By default Miri isolates the program from the host so that execution is fully deterministic. Anything that would reach outside gets replaced: entropy gathering for random number generators, environment variables and clocks are all served by fake implementations. For a test suite the payoff is that a run today and a run next month produce the same values, which removes a whole class of flaky report.
Two warnings come attached to that design. The first is that the isolation is not a proper sandbox, and gaps in it count as normal bugs rather than security bugs, so pointing Miri at code you would not run is a bad idea. The second is that the fake system RNG APIs make Miri not suited for cryptographic use, and the project states plainly that keys must not be generated under it.
The escape hatch is one flag. Setting MIRIFLAGS="-Zmiri-disable-isolation" puts the program back in touch with the real system APIs, and the determinism guarantee leaves with it. There is a compile-time counterpart too: when code is compiled through cargo miri, the cfg(miri) config flag is set, so a crate can branch on running under the interpreter rather than on real hardware.
One execution out of many, and loom covers what Miri samples
Every verdict Miri returns belongs to a single execution, and the project is direct about what that costs. If a program depends on where allocations happen to land, or on the exact interleaving of concurrent threads, Miri tests one of many possible executions and misses the bugs that live in another. Running with different values of -Zmiri-seed widens the sampling, and by the project's own account still comes far short of exploring every execution.
Layout assumptions are a separate trap. Code that relies on unspecified details of how data is laid out runs fine under Miri and can still break, including by causing undefined behavior, on a different compiler version or a different platform. -Zrandomize-layout exists to surface some of those cases.
That is where the difference from loom becomes concrete. For complicated atomic code the project points at loom, linked in its documentation as tokio-rs/loom, rather than at Miri. The two approaches do different jobs: loom models the concurrency and explores schedules, while Miri interprets the real program under one schedule at a time.
A clean run means this execution stayed in bounds, not that code is sound
Miri fundamentally cannot tell you that your code is sound, and the distinction is worth stating before any feature list. Soundness is the property of never causing undefined behavior when invoked from arbitrary safe code, even in combination with other sound code. Miri observes something narrower: whether one particular way of interacting with your code, a test suite for instance, triggers undefined behavior in one particular execution, and there may be many executions.
The asymmetry still makes the tool useful. When Miri finds undefined behavior, the code is unsound, with no further argument needed. When it finds nothing, all that has been established is that this execution of this input set stayed inside the rules. The remaining work is more inputs and more of the non-deterministic choices, which is the same conclusion the concurrency caveats reach from a different direction.
A second limit sits on the definition itself. There is no formal specification of undefined behavior in Rust, so Miri works from its own approximation of it, and the official wording lives in the Rust Reference. Miri is updated alongside the compiler to catch undefined behavior as the current compiler understands it, and it makes no promises about future versions of rustc.
Installed as a nightly component and driven through cargo miri
Miri ships as a component of the nightly toolchain, so installing it is a rustup step and nothing more:
rustup +nightly component add miriThe commands that follow assume nightly is the pinned toolchain, which an override arranges. Running cargo +nightly on each command instead is the documented alternative:
rustup override set nightlyFrom there the project runs through cargo, and cargo miri run and cargo miri test accept exactly the same flags as cargo run and cargo test:
cargo miri test
cargo miri runThe first invocation is slower than the rest, because Miri performs extra setup and installs some dependencies on that pass, asking for confirmation before installing anything. A CI image has to expect that prompt. Filtering behaves as it does for cargo test, so cargo miri test filter runs only the tests with filter in their name.
Miri's own flags travel in MIRIFLAGS instead of on the command line, which is how a check gets disabled for one run:
MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri runThe crate manifest is short enough to read in one pass: name miri, version 0.1.0, edition 2024, default-run set to miri, and both test and doctest disabled for the binary target.
No releases, version 0.1.0, and a manifest that argues with the README
Some of what the repository says about itself is out of step with what it ships. There are no GitHub releases at all, and the manifest version is 0.1.0 for a tool that tracks nightly rustc, so the version number tells you nothing about which compiler a build expects. The default branch is master rather than main.
The manifest also sits oddly against the prose. It pulls mio with the os-poll and net features plus socket2, while the README states that Miri currently does not support networking. On Unix it offers libffi and libloading as optional native-lib dependencies, and on x86 GNU Linux it adds an optional nix with mman, ptrace and signal. The README says most platform-specific APIs and FFI are unavailable and recommends --target x86_64-unknown-linux-gnu when running on Windows.
Licensing is dual, with MIT OR Apache-2.0 in the manifest and both LICENSE-MIT and LICENSE-APACHE at the root, while the licence field on the repository shows Apache-2.0 alone. Alongside the interpreter sit sibling directories: cargo-miri/, miri-script/, priroda/ and genmc-sys/, plus bench-cargo-miri/, test-cargo-miri/ and a ci/ directory.
Editorial conclusion
Run Miri in CI if you maintain unsafe code, because cargo miri test turns pointer mistakes, leaks and aliasing violations into build failures on the test suite you already have. Do not reach for it to certify soundness, to test networked or FFI-heavy code, or to generate keys, since the fake randomness and the missing system APIs rule all three out. Before wiring it into a pipeline, check three things: that the nightly toolchain is pinned with rustup override set nightly rather than tracked loosely, that the first run's dependency install and confirmation prompt are handled in the image, and that loom is running alongside it if the code under test leans on atomics, because Miri explores one execution per seed.
Frequently asked questions
How do I install Miri for a Rust project?
Miri ships as a component of the nightly toolchain, and the install step is rustup +nightly component add miri. The commands that follow assume nightly is pinned with rustup override set nightly, or you can use cargo +nightly on each command instead.
Why does Miri replace the system random number generator?
Miri isolates the program from the host so that execution stays deterministic, replacing entropy gathering, environment variables and clocks with fake implementations. The project states that these fake system RNG APIs make Miri not suited for cryptographic use, so keys must not be generated under it.
Does a clean Miri run prove my crate is sound?
No. Miri checks whether one particular interaction with your code, such as a test suite, causes undefined behavior in one particular execution. It also notes that it works from its own approximation of undefined behavior rather than a formal Rust specification, and that it makes no promises about future versions of rustc.
Does Miri support networking and foreign function calls?
The README states that Miri currently does not support networking, and that it runs as a platform-independent interpreter with no access to most platform-specific APIs or FFI. A few APIs are implemented, including printing to stdout, environment variables and basic file system access, and on Windows it suggests passing --target x86_64-unknown-linux-gnu for better support.
Official sources
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.
[](https://hysenlabs.com/projects/rust-lang-miri)Star history chart for your README
[](https://hysenlabs.com/projects/rust-lang-miri)Was this useful?