How to do you read Miri errors

How do you decipher the error messages of Miri?
Promptet by this thread

I tried to comprehend the following Miri error message:

Compiling playground v0.0.1 (/playground)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.64s
     Running `/playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/playground`
error: Undefined Behavior: attempting a read access using <3563> at alloc1606[0x0], but that tag does not exist in the borrow stack for this location
  --> src/main.rs:11:9
   |
11 |         core::ptr::copy(src_ptr, dst_ptr, 8);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         attempting a read access using <3563> at alloc1606[0x0], but that tag does not exist in the borrow stack for this location
   |         this error occurs as part of an access at alloc1606[0x0..0x8]
   |
   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <3563> was created by a SharedReadOnly retag at offsets [0x0..0x10]
  --> src/main.rs:9:34
   |
9  |         let src_ptr: *const u8 = bytes.as_ptr().cast();
   |                                  ^^^^^^^^^^^^^^
help: <3563> was later invalidated at offsets [0x0..0x10] by a Unique retag
  --> src/main.rs:10:23
   |
10 |         let dst_ptr = bytes.as_mut_ptr().cast::<u8>().add(8);
   |                       ^^^^^
   = note: BACKTRACE (of the first span):
   = note: inside `main` at src/main.rs:11:9: 11:45

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

Produced by running this code

use core::mem::MaybeUninit;

fn main() {
    let mut bytes: [MaybeUninit<u8>; 16] = [MaybeUninit::uninit(); 16];
    for i in 0..16 {
        bytes[i] = MaybeUninit::new(i as u8);
    }
    unsafe {
        let src_ptr: *const u8 = bytes.as_ptr().cast();
        let dst_ptr = bytes.as_mut_ptr().cast::<u8>().add(8);
        core::ptr::copy(src_ptr, dst_ptr, 8);
    }
    for i in 0..16 {
        println!("{}", unsafe{ bytes[i].assume_init()});
    }

}

It mentiones tags which I believe <3563> is an example of and allocations like alloc1606[0x0]. But I don't see a way of mapping these concepts back to the source code in order to be able to find out what went wrong. What is a tag? Is it a variable? And how would I find out which one? And how can I track where an allocation originated?

The solution to this Miri error turned out to be that if you want mutiple *mut and *const pointers you need to derive them from a single *mut pointer obtained through a &mut reference. But how would you deduce this from the miri error? Can you give me some tips how you would go about reading this error?

1 Like

To really understand the error, you have to understand how miri works internally. For example you might want to read this:

https://perso.crans.org/vanille/treebor/

A tag is a piece of information that each pointer has. It determines the permissions of the pointer. To use a pointer it must have the right tag.

5 Likes

Does Miri already operate on tree borrows? The error seems to indicate it uses the stacked borrows model.

That is configurable. I'm not sure which is the default right now.