🎓 A1Learner
🔍

Rust Miri

Miri is an interpreter that runs a Rust program step by step and watches its memory. It reports undefined behaviour at the moment it happens.

Install it (nightly Rust)

Miri ships with the nightly toolchain, not the stable one:

// in your terminal:
// $ rustup +nightly component add miri
// $ cargo +nightly miri setup

Run your tests under it

One command runs your test suite inside the interpreter instead of on the real machine:

// in your terminal:
// $ cargo +nightly miri test

What it catches

Miri stops at the exact line. A rule breaks there. Miri names the rule. It may be an out-of-bounds read. It may be a use after free. It may be a data race. It may be an invalid value:

// error: Undefined Behavior: out-of-bounds pointer arithmetic
// (the report names the operation and shows the code that caused it)

The kind of line Miri stops on

This pointer step is ordinary Rust to write. Only a run under Miri shows whether every step stays in bounds:

let data = vec![10, 20, 30];
let ptr = data.as_ptr();
// reading one step past the end here would be undefined behaviour

Example

fn main() {
    let data = vec![10, 20, 30];
    let ptr = data.as_ptr();
    let len = data.len();
    // SAFETY: ptr and len came from the live Vec above, which outlives this block.
    let view = unsafe { std::slice::from_raw_parts(ptr, len) };
    println!("view = {:?}", view);
    println!("sum = {}", view.iter().sum::<i32>());
}

Key points

  • Miri: an interpreter for Rust programs that watches memory while the code runs and reports undefined behaviour as it happens
  • Miri is much slower than a normal run.
  • It interprets every step.
  • So aim it at tests.
  • Do not aim it at whole applications
  • Most foreign (C) calls cannot run under Miri: code that crosses the FFI border is mostly outside its view
  • A clean Miri run is not a proof: it shows only that the runs you tried behaved.
  • Different inputs may walk different paths
  • Aim Miri at the small unsafe core of a program.
  • Reading alone cannot fully prove unsafe code, so programmers run it under Miri.

In this example, a slice is rebuilt. It is rebuilt from a raw pointer and a length. That is the from_raw_parts pattern. The Raw Pointers lesson showed it. Both facts come from the live Vec. That Vec sits on the lines above. So the rebuild is sound. The view prints [10, 20, 30]. The sum is 60. This is exactly the kind of code for Miri. Run it with cargo +nightly miri test. Miri interprets it. It watches every pointer step. Change len to data.Use len() + 1 instead. A normal run may still print something plausible. But under Miri the run stops. It reports the out-of-bounds read. It reports it as undefined behaviour.

Note

The safe code around it never needs checking — and small unsafe blocks keep the slow run short.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Miri runs your whole test suite and reports nothing. What have you shown?