
Miri: Interpreter for Rust's mid-level intermediate representation - ingve
https://github.com/rust-lang/miri
======
steveklabnik
As of today, you can easily install miri on the nightly channel, which is why
this is popping up today.

    
    
      $ rustup component add miri —toolchain=nightly
    

You can then “cargo +nightly miri test” to run your tests under miri. You
probably should “cargo clean” first.

Of course you can skip the explicit nightly flags in those commands if your
default toolchain is nightly.

~~~
bluejekyll
Would it be accurate to say that running Miri against your code might catch
some issues in unsafe code that the standard compiler will not?

~~~
shepmaster
Pedantically, it's running your code via Miri. Miri is an interpreter, so you
have to trigger undefined behavior during a specific run of Miri for it to be
reported.

~~~
bluejekyll
I think that’s a fair point. It sounds like it’s possible with Miri to write
unit tests for specific UB concerns, and it’s more likely to catch them than
standard unit tests. This will be useful.

~~~
shepmaster
I haven't put any thought or time into it yet, but I assume you could combine
it with a fuzzer. cargo-fuzz [1] already does this with LLVM's sanitizers, but
I'm thinking something like proptest [2] could be used to help find those
specific inputs and run under miri.

It _may_ even be as simple as creating the normal proptest tests and then
doing `cargo miri --test` or something similar!

[1]: [https://github.com/rust-fuzz/cargo-fuzz](https://github.com/rust-
fuzz/cargo-fuzz)

[2]: [https://crates.io/crates/proptest](https://crates.io/crates/proptest)

~~~
staticassertion
Fuzzing + miri sounds like it will be incredibly slow. Probably slower than
just using asan.

The good thing about UB in rust is it's easy to target - you don't need to
explore the whole space, just module boundaries that encapsulate that space.
So something more fine grained should be fine with miri, and fuzzing can
capture more complex errors like panics.

~~~
shepmaster
Yes, that's what I mean about using proptest (if you are unfamiliar, it's akin
to QuickCheck). You'd write the proptest functions for your dedicated unsafe
section.

As an example, I use this with Jetscii [1] in an attempt to throughly test
SIMD vs non-SIMD code. If Miri detected errors with SIMD (I don't think it
does now), then I could run those tests inside of Miri, having them pull
double duty.

[1]:
[https://github.com/shepmaster/jetscii/](https://github.com/shepmaster/jetscii/)

~~~
staticassertion
Oh, yes, agreed that proptest is way more viable here and probably a good
solution to pair with miri.

------
roblabla
I often find myself yearning for a Rust REPL. I wonder if it'd be possible to
run the rust compiler to lower the code down to MIR, and then run that in the
Miri interpreter?

~~~
navaati
I think the problem is not really evaluating Rust code on the fly: you could
just as well compile it on the fly and execute is, this is what the Haskell
interpreter ghci does.

The difficulty is more in defining what it means to enter code one line at a
time, what about context, scope, lifetimes, all these stuff.

If a more knowledgeable Rust team member can confirm…

~~~
richardwhiuk
I think some of the typing would be problematic, as it allows action at a
distance.

e.g.

    
    
      fn bar(i : &u16) {
         i + 1;
      }
    
      let a = 8; // a is a u8.
    
      bar(&a); // Now a needs to be a u16,
               // but we executed the line of code
               // above, which forced it to be u8.
               // Is this a type error?
    

You can probably solve this, by requiring all blocks of code to be executed
repl style to be standalone.

i.e. the above code is entered as:

    
    
      fn bar(i : &u16) {
         i + 1;
      }
    
      ## Execution happens in the REPL
    
      {
          let a = 8;
    
          bar(&a);
      }
    
      ## Execution happens in the REPL.
    

And make the far above give "a not defined in this scope"

~~~
gpm
I think you solve this by just giving an error

In [0] fn bar(i: &u16) { i + 1 }

In [1] let a = 8; // a is a i32.

In [2] bar(&a)

Out[2] Type error: a is a i32 and it needs to be a u16

In [3] let a: u16 = 8; // The original a is shadowed by the new a, the new a
is a u16

In [4] bar(&a)

Out[4] 9

Or a similar situation where there isn't enough information to infer a type

In [0] let x = Vec::new();

Out[0] Type error: Need to know the type of x

In [1] let x: Vec<bool> = Vec::new();

In [2] x.push(true); // Note that if we had entered this at the same tiem as
Vec::new() the type could have been inferred.

~~~
richardwhiuk
That forces you to write very unnatural Rust - it makes it a much clunkier
language.

------
gregwebs
Are there any plans to use an interpreter towards having a fast compile cycle.
My reference point is Haskell's ghci which reloads changes very quickly
(whereas the normal compiler is similar in speed to Rust).

~~~
steveklabnik
Not particularly; or rather, this doesn't help with that directly. There are
other, bigger fish to fry first.

------
shepmaster
For a quick way to play with Miri, you can use the Rust playground [1]. Enter
some code and then select Tools > Miri.

As a starting example, here's some code that Miri reports as violating Rust's
rules for references [2]:

    
    
        fn main() {
            let mut i = 42;
            
            unsafe {
                let a = &mut *(&mut i as *mut _);
                let b = &mut *(&mut i as *mut _);
        
                println!("{:p}, {:p}", a, b);
            }
        }
    

The output is:

    
    
        error[E0080]: constant evaluation error: Borrow being dereferenced (Uniq(1797)) does not exist on the borrow stack
            --> /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/fmt/mod.rs:2050:24
             |
        2050 |         Pointer::fmt(&(&**self as *const T), f)
             |                        ^^^^^^^ Borrow being dereferenced (Uniq(1797)) does not exist on the borrow stack
             |
             = note: inside call to `<&mut i32 as std::fmt::Pointer>::fmt` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/fmt/mod.rs:1016:17
             = note: inside call to `std::fmt::write` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/io/mod.rs:1266:15
             = note: inside call to `<std::io::StdoutLock as std::io::Write>::write_fmt` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/io/stdio.rs:493:9
             = note: inside call to `<std::io::Stdout as std::io::Write>::write_fmt` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/io/stdio.rs:737:9
             = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/thread/local.rs:299:16
             = note: inside call to `std::thread::LocalKey::<std::cell::RefCell<std::option::Option<std::boxed::Box<dyn std::io::Write + std::marker::Send>>>>::try_with::<[closure@DefId(1/1:1025 ~ std[82ff]::io[0]::stdio[0]::print_to[0]::{{closure}}[0]) 0:&std::fmt::Arguments, 1:&fn() -> std::io::Stdout], std::result::Result<(), std::io::Error>>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/io/stdio.rs:731:18
             = note: inside call to `std::io::stdio::print_to::<std::io::Stdout>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/io/stdio.rs:753:5
        note: inside call to `std::io::_print` at <::std::macros::println macros>:2:3
            --> src/main.rs:8:9
             |
        8    |         println!("{:p}, {:p}", a, b);
             |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
             = note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:34
             = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:53
             = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:293:40
             = note: inside call to `std::panicking::try::do_call::<[closure@DefId(1/1:1830 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:289:5
             = note: inside call to `std::panicking::try::<i32, [closure@DefId(1/1:1830 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:388:9
             = note: inside call to `std::panic::catch_unwind::<[closure@DefId(1/1:1830 ~ std[82ff]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:25
             = note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:64:5
             = note: inside call to `std::rt::lang_start::<()>`
             = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
    
    

[1]: [https://play.rust-lang.org/](https://play.rust-lang.org/)

[2]: [https://doc.rust-lang.org/stable/book/ch04-02-references-
and...](https://doc.rust-lang.org/stable/book/ch04-02-references-and-
borrowing.html#the-rules-of-references)

~~~
wyldfire
> As a starting example, here's some code that Miri reports as violating
> Rust's rules for references

So...this was determined statically? Or "statically"? If there's no/few false
positives could this be used as a check in rustc? Or clippy?

BTW for this one specifically, why can't we look at the AST and see that a and
b alias?

EDIT: I guess it's because it's lost in the deref/take addr?

~~~
gpm
It was determined dynamically, determining it statically with no false
positives is impossible.

Try sticking a `println!("Hello");` statement at the start of main, it will
get run before we run into this error.

~~~
wyldfire
> It was determined dynamically

So it looks like the defects that miri can detect overlaps heavily with the
existing sanitizers (but I see at least one language-specific constraint that
isn't covered by sanitizers). I suppose it's nice not to have the
hassle/complexity of the sanitizers (especially nice not to have to deal with
the ODR noise from UBSan), but is the intent that miri could find much beyond
what the sanitizers could?

Regarding aligned accesses: does miri consider the alignment constraints of
the most restrictive target? Or are there language specifications regarding
alignment?

To my uneducated eye, it looks like many/most of these defects that miri
detects require unsafe to cause (barring compiler defects). Is that accurate?

~~~
steveklabnik
> is the intent that miri could find much beyond what the sanitizers could?

Miri serves two purposes. One of them is completely unrelated to this stuff,
and that's to deal with const functions.

This stuff, on the other hand, yes. Those tools don't know anything about the
language themselves. Miri is able to model the exact rules that we lay out for
pointer usage. This means that it should be a lot better, not only at finding
things, but also stuff like error messages, eventually.

> Regarding aligned accesses: does miri consider the alignment constraints of
> the most restrictive target? Or are there language specifications regarding
> alignment?

Because it's an interpreter, miri can run like any target. That said, I'm not
100% sure that 'cargo miri' exposes this easily yet; I gave it a shot but it's
giving me an interesting error. The idea is that you'd specify the target
you're testing for.

> To my uneducated eye, it looks like many/most of these defects that miri
> detects require unsafe to cause (barring compiler defects). Is that
> accurate?

Yes. Safe code should never produce undefined behavior, that's a hard design
constraint of Rust.

------
emiliobumachar
I initially misread the headline and thought that the Machine Learning
Research Institute had made progress in deobfuscating AI decision processes
for human scrutiny.

