GithubHelp home page GithubHelp logo

rust-lang / miri Goto Github PK

View Code? Open in Web Editor NEW
4.1K 57.0 312.0 16.98 MB

An interpreter for Rust's mid-level intermediate representation

License: Apache License 2.0

Rust 99.30% Python 0.33% Shell 0.32% C 0.02% Batchfile 0.03%

miri's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

miri's Issues

debugging/stepping

I'd like to use miri to step through code like in a debugger. At first I thought it would make sense to restructure miri so the interpreter ends up looping over an iterator that does the actual processing. But now I wonder if it would make sense to insert "hooks" into random places inside the interpreter, which a caller to the interpreter could attach to. So basically the interpreter runs in a thread and whenever it encounters a hook-place, it checks for a hook and if one is there, starts interacting with it.

The hook has a few options available:

  • obtain information about a stack frame
  • obtain the stack frame list
  • read the memory behind a pointer
  • execute an arbitrary MIR statement.
  • stop the interpreter
  • allow the interpreter to continue
  • add/remove hooks
  • step to the next hook-place

Possible hook-places:

  • reaching a file:line (requires checking spans in the interpreter)
  • changes a memory location gotten during another hook-processing
  • interpretation errors (unknown intrinsic, overflow, UB)

What do you think?

multi-argument closure virtual calls are broken

Source

From this rustc test.

fn foo(f: &mut FnMut(isize, isize) -> isize) -> isize {
    f(1, 2)
}

fn main() {
    let z = foo(&mut |x, y| x * 10 + y);
    assert_eq!(z, 12);
}

Output and error

TRACE:miri::step:   _0 = <std::ops::FnMut(isize, isize) -> isize as std::ops::FnMut<(isize, isize)>>::call_mut(_3, _4) -> bb1
TRACE:miri::eval_context:   _1 (1 frames up): Undef
TRACE:miri::eval_context:   _3: (Ptr(Pointer { alloc_id: AllocId(0), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(3), offset: 0 }))
TRACE:miri::memory:   Alloc 3:    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (40 bytes) (immutable)
TRACE:miri::memory:                                                                                       └─────────(2)─────────┘ └─────────(2)─────────┘ 
TRACE:miri::memory:   Alloc 2:     function pointer: main::{{closure}}: extern "rust-call" fn((isize, isize)) -> isize
TRACE:miri::eval_context:   _4:
TRACE:miri::memory:   Alloc 4:    01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 (16 bytes)
DEBUG:miri::memory: reading fn ptr: 2
TRACE:miri::eval_context:   load mir DefId { krate: CrateNum(0), node: DefIndex(7) => overloaded_calls_object_two_args/4089d7c8b778d88cec885baf7b69e6df-exe::main[0]::{{closure}}[0] }
TRACE:miri::eval_context:    _1: Undef
TRACE:miri::eval_context:    _2: Undef
TRACE:miri::step:    // bb0
TRACE:miri::step:    StorageLive(_4)
TRACE:miri::step:    _4 = _2
TRACE:miri::eval_context:    _4: Undef
TRACE:miri::eval_context:    _2:
TRACE:miri::memory:    Alloc 5:    01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context:    _4: Bytes(1)
TRACE:miri::step:    StorageLive(_5)
TRACE:miri::step:    _5 = _3
TRACE:miri::eval_context:    _5: Undef
TRACE:miri::eval_context:    _3: Undef
TRACE:miri::eval_context:    _5: Undef
TRACE:miri::step:    StorageLive(_6)
TRACE:miri::step:    StorageLive(_7)
TRACE:miri::step:    _7 = _4
TRACE:miri::eval_context:    _7: Undef
TRACE:miri::eval_context:    _4: Bytes(1)
TRACE:miri::eval_context:    _7: Bytes(1)
TRACE:miri::step:    _8 = CheckedMul(_7, const 10isize)
TRACE:miri::eval_context:    _8: Undef
TRACE:miri::eval_context:    _7: Bytes(1)
TRACE:miri::eval_context:    _8: (Bytes(10), Bytes(0))
TRACE:miri::step:    assert(!(_8.1: bool), "attempt to multiply with overflow") -> bb1
TRACE:miri::step:    // bb1
TRACE:miri::step:    _6 = (_8.0: isize)
TRACE:miri::eval_context:    _6: Undef
TRACE:miri::eval_context:    _6: Bytes(10)
TRACE:miri::step:    StorageLive(_9)
TRACE:miri::step:    _9 = _5
TRACE:miri::eval_context:    _9: Undef
TRACE:miri::eval_context:    _5: Undef
TRACE:miri::eval_context:    _9: Undef
TRACE:miri::step:    _10 = CheckedAdd(_6, _9)
TRACE:miri::eval_context:    _10: Undef
TRACE:miri::eval_context:    _6: Bytes(10)
TRACE:miri::eval_context:    _9: Undef
error: attempted to read undefined bytes
  --> ../rust/master/src/test/run-pass/overloaded-calls-object-two-args.rs:20:29
   |
20 |     let z = foo(&mut |x, y| x * 10 + y);
   |                             ^^^^^^^^^^

large repeat expressions don't adhere to the execution limit

and are really slow, since they use write_value for every single value.

I have some code where I write_value the first value and then just copy those bytes N-1 times over the remaining array fields. Better ideas are very welcome.

old speed:

test repeat        ... bench:     509,773 ns/iter (+/- 96,298)
test repeat_manual ... bench: 313,843,922 ns/iter (+/- 5,732,707)

new speed:

test repeat        ... bench:     366,520 ns/iter (+/- 69,298)
test repeat_manual ... bench: 300,833,908 ns/iter (+/- 8,937,121)

not really worth the improvements. I'll just slap on an execution limit thingy.

add checks to the `transmute` intrinsic to only allow "safe" transmutes

examples of unsafe transmutes:

  • 32-bit values that aren't valid unicode indices casted to char
  • &T -> &mut T
  • null value to a NonZero type

examples of transmutes that should be something else (warn?)

  • integer -> *const T (cast)
  • &T -> *const T (cast/coercion) (linted by clippy)
  • *const T -> &T (should be a deref) (linted by clippy)

immutable static with raw pointer to mutable static makes mutable static immutable

a non-mutable static might contain a raw pointer into a static mut and this code would then freeze the static mut. Don't worry about it for now.

this code: #85 (comment)

Code example reproducing this issue:

#![allow(dead_code)]

static mut FOO: i32 = 42;
static BAR: Foo = Foo(unsafe { &FOO as *const _} );

struct Foo(*const i32);

unsafe impl Sync for Foo {}

fn main() {
    unsafe {
        assert_eq!(*BAR.0, 42);
        FOO = 5;
        assert_eq!(FOO, 5);
        assert_eq!(*BAR.0, 5);
    }
}

handle small optimized `Option` values

probably just requires making read_nonnull_discriminant_value also take in the discriminant type instead of assuming it's usize sized.

The following code reproduces the issue:

// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

use std::mem::size_of;

enum E {
    A = 1,
    B = 2,
    C = 3,
}

struct S {
    a: u16,
    b: u8,
    e: E,
}

fn main() {
    assert_eq!(size_of::<E>(), 1);
    assert_eq!(size_of::<Option<E>>(), 1);
    assert_eq!(size_of::<Result<E, ()>>(), 1);
    assert_eq!(size_of::<S>(), 4);
    assert_eq!(size_of::<Option<S>>(), 4);
    let enone = None::<E>;
    let esome = Some(E::A);
    if let Some(..) = enone {
        panic!();
    }
    if let None = esome {
        panic!();
    }
}

minimal reproduction:

#![allow(dead_code)]

enum E {
    A = 1,
    B = 2,
    C = 3,
}

fn main() {
    let enone = None::<E>;
    if let Some(..) = enone {
        panic!();
    }
}

Support floating point types f32 and f64

Support floating point types. Probably have to add both f32, and f64 to PrimVal, and handle those everywhere.

AFAIK Rust floating point arithmetic follows IEEE754. Still, I don't know what happens on architectures that do not fully conform to the spec. Does Rust or LLVM emulate it or not? Probably not. This might turn out to not be a problem in practice (e.g. miri result would still be deterministic "enough of the time"). Once we are the we might want to reconsider that though.

fn item as closure trait object "index out of bounds"

Source

fn foo() {}

fn main() {
    let f: &Fn() = &foo;
    f();
}

Output and error

TRACE:miri::step 0  _4 = <std::ops::Fn() as std::ops::Fn<()>>::call(_5, _6) -> bb1
TRACE:miri::eval_context 0  frame[0] _4: Undef
TRACE:miri::eval_context 0  frame[0] _5: (Ptr(Pointer { alloc_id: AllocId(0), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(3), offset: 0 }))
TRACE:miri::memory 0  Alloc 3:    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (48 bytes) (immutable)
TRACE:miri::memory 0                                                                                      └─────────(2)─────────┘ └─────────(2)─────────┘ └─────────(2)─────────┘                                    
TRACE:miri::memory 0  Alloc 2:     function pointer
TRACE:miri::eval_context 0  frame[0] _6:
DEBUG:miri::memory: reading fn ptr: 2
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', src/terminator/mod.rs:632

optimize zst to not require an actual allocation

I'm not sure, but I think simply reserving a single allocation to be used for all zst should work (pointers to different zst values will compare equal, but comparing pointers to different types is probably forbidden anyway).

recursive static initialization is broken

I investigated the only "tried to modify constant memory" in #55 and it's due to the fact that Miri can't properly initialize statics that recursively refer to each other. This would be low priority since it only affects the unstable #![feature(static_recursion)], but the feature looks like it will be stable soon.

E.g.

struct S(&'static S);
static S1: S = S(&S2);
static S2: S = S(&S1);

When you start initializing S1:

  1. you'll start initializing S2 because it's required (ConstantExtractor)
  2. write the final result for S2
  3. freeze S2 and all allocations it references (this freezes S1)
  4. pick up where you left off initializing S1
  5. write the final result for S1 (fails because it's frozen)

substs order of drop impl from `impl` and not from the type

Note how the drop impl swaps the generics: impl<V, U> Drop for S<U, V>.

use std::mem;
struct S<U,V> {
    _u: U,
    size_of_u: usize,
    _v: V,
    size_of_v: usize
}


impl<U, V> S<U, V> {
    fn new(u: U, v: V) -> Self {
        S {
            _u: u,
            size_of_u: mem::size_of::<U>(),
            _v: v,
            size_of_v: mem::size_of::<V>()
        }
    }
}

impl<V, U> Drop for S<U, V> {
    fn drop(&mut self) {
        assert_eq!(mem::size_of::<U>(), self.size_of_u); // mem::size_of::<U>() is 2 instead of 1
        assert_eq!(mem::size_of::<V>(), self.size_of_v);
    }
}

fn main() {
    S::new(0u8, 1u16);
}

drop in `Box<Box<T>>` as `Box<Trait>` broken

rust-lang/rust#39230 changed some things around with boxed, especially that there is a "Drop impl" now: https://github.com/rust-lang/rust/blame/master/src/liballoc/boxed.rs#L297 (it's empty, doesn't drop its contents)

We should just wait for rustc to fix it.

repro:

#![feature(box_syntax)]

struct DroppableStruct;

static mut DROPPED: bool = false;

impl Drop for DroppableStruct {
    fn drop(&mut self) {
        unsafe { DROPPED = true; }
    }
}

trait MyTrait { fn dummy(&self) { } }
impl MyTrait for Box<DroppableStruct> {}

struct Whatever { w: Box<MyTrait+'static> }
impl  Whatever {
    fn new(w: Box<MyTrait+'static>) -> Whatever {
        Whatever { w: w }
    }
}

fn main() {
    {
        let f: Box<_> = box DroppableStruct;
        let _a = Whatever::new(box f as Box<MyTrait>);
    }
    assert!(unsafe { DROPPED });
}

Box is deallocated before drop impl is run

struct Foo(Box<u8>);
impl Drop for Foo {
    fn drop(&mut self) {
        *self.0 = 255; // error: dangling pointer dereferenced
    }
}

fn main() {
    drop(Foo(Box::new(0)));
}

Boxes are immediately deallocated by the drop terminator, where as drop implementations are pushed to the stack, https://github.com/solson/miri/blob/master/src/interpreter/terminator/mod.rs#L531
I'm not sure how to solve this, other than maybe adding a separate type of frame just to deallocate the box.

miri doesn't drop values moved into closures with `let _ = x`

The following code works fine if drop(x) is used instead of let _ = x. The closure function generated contains just a _0 = () and a return. Trans probably does some magic, but I haven't been able to locate it.

struct Foo<'a>(&'a mut bool);

impl<'a> Drop for Foo<'a> {
    fn drop(&mut self) {
        *self.0 = true;
    }
}

fn f<T: FnOnce()>(t: T) {
    t()
}

fn main() {
    let mut ran_drop = false;
    {
        let x = Foo(&mut ran_drop);
        let x = move || { let _ = x; };
        f(x);
    }
    assert!(ran_drop);
}

Implement the rest of the possible casts

Cast kinds:

  • Unsize from array pointer to slice pointer
  • Unsize to trait object pointer¹
  • Reify function pointer
  • Everything else²

¹ This goes along with implementing vtables and virtual method calls.

² Currently casts with same-sized source and destination are implemented by a blind copy and casts from fat pointers copy just the pointer part. This works well enough to pass most of our existing tests but it's extremely hacky. Search for the Misc case.

This will likely require multiple PRs.

upstream to rustc

Leaving things to do here so they don't get lost

@eddyb in IRC:

replace the LLVM-generating stuff in trans with lowering miri allocations to LLVM
it should be more efficient in cases without relocations because you can give LLVM a byte blob
not sure if its own load-from-constant folding can deal with it

Use rustc's compiler error macros.

Miri should use rustc's compiler error macros to ease the eventual merge into rustc itself.

So, instead of panic! or unimplemented!, use span_bug!, bug!, etc. I believe these come from #[macro_use] extern crate rustc;.

Prompted by #28 (comment).

Document necessary environment variables for use without rustup

Tried to run a test with cargo run tests/run-pass/vecs.rs, but it results on several error messages stating that the following libraries cannot be found (note: I have removed the trailing hashes in the names for clarity):

  • rustc_driver.dll
  • rustc.dll
  • rustc_mir.dll
  • rustc_borrowck.dll
  • (and a lot more)

The same happens when running miri directly.

When looking at miri.rs I found out that it relies on RUSTUP_HOME and RUSTUP_TOOLCHAIN. After adding those environment variables everything worked correctly. I think this should be documented on the readme.

wrong field access on unsized types

The f.b access goes to offset 2, while it should be offset 8 due to the alignment of T. The issue is that in eval_lvalue_projection the base_ty is Foo<Bar>, so the layout computed by rustc says "hey we can put T directly after a without padding". I'm completely baffled as to how rustc does these things. Is there some info in the vtable we can use?

struct Foo<T: ?Sized> {
    a: u16,
    b: T
}

trait Bar {
    fn get(&self) -> usize;
}

impl Bar for usize {
    fn get(&self) -> usize { *self }
}

fn main() {
    let f : Foo<usize> = Foo { a: 0, b: 11 };
    let f : &Foo<Bar> = &f;
    assert_eq!(f.b.get(), 11);
}
TRACE:miri::eval_context: load mir DefId { krate: CrateNum(0), node: DefIndex(13) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::main[0] }
TRACE:miri::step:  StorageLive(_1)
TRACE:miri::step:  _1 = Foo<usize> { a: const 0u16, b: const 11usize }
TRACE:miri::eval_context:  _1: Undef
TRACE:miri::memory:  allocate: 16, 8 at AllocId(2)
TRACE:miri::eval_context:  _1:
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step:  StorageLive(_2)
TRACE:miri::step:  StorageLive(_3)
TRACE:miri::step:  StorageLive(_4)
TRACE:miri::step:  _4 = &_1
TRACE:miri::eval_context:  _4: Undef
TRACE:miri::eval_context:  _1:
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context:  _4: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step:  _3 = &(*_4)
TRACE:miri::eval_context:  _3: Undef
TRACE:miri::eval_context:  _4: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context:  _4: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::lvalue:  deref to Foo<usize> on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }))
TRACE:miri::eval_context:  _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step:  _2 = _3 as &Foo<Bar> (Unsize)
TRACE:miri::eval_context:  _2: Undef
TRACE:miri::eval_context:  _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
DEBUG:miri::vtable: get_vtable(trait_ref=Binder(<usize as Bar>))
DEBUG:miri::vtable: get_vtable_methods(impl_id=DefId { krate: CrateNum(0), node: DefIndex(10) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0] }, substs=Slice([])
DEBUG:miri::vtable: get_vtable_methods: trait_method_type=AssociatedItem { def_id: DefId { krate: CrateNum(0), node: DefIndex(8) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0]::get[0] }, name: get(88), kind: Method, vis: Restricted(DefId { krate: CrateNum(0), node: DefIndex(0) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe }), defaultness: Default { has_value: false }, container: TraitContainer(DefId { krate: CrateNum(0), node: DefIndex(7) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0] }), method_has_self_argument: true }
DEBUG:miri::vtable: get_vtable_methods: trait_method_type=AssociatedItem { def_id: DefId { krate: CrateNum(0), node: DefIndex(8) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0]::get[0] }, name: get(88), kind: Method, vis: Restricted(DefId { krate: CrateNum(0), node: DefIndex(0) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe }), defaultness: Default { has_value: false }, container: TraitContainer(DefId { krate: CrateNum(0), node: DefIndex(7) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::Bar[0] }), method_has_self_argument: true }
DEBUG:miri::vtable: get_vtable_methods: mth=ImplMethod { method: AssociatedItem { def_id: DefId { krate: CrateNum(0), node: DefIndex(11) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0]::get[0] }, name: get(88), kind: Method, vis: Public, defaultness: Final, container: ImplContainer(DefId { krate: CrateNum(0), node: DefIndex(10) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0] }), method_has_self_argument: true }, substs: Slice([]), is_provided: false }
DEBUG:miri::memory: creating fn ptr: 3
TRACE:miri::memory:  allocate: 32, 8 at AllocId(4)
TRACE:miri::memory:  mark_static_initalized
TRACE:miri::memory:  Alloc 4:    00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes)
TRACE:miri::memory:                                                                                      └─────────(3)─────────┘ 
TRACE:miri::memory:  Alloc 3:     function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::memory:  sub-reloc: AllocId(3)
TRACE:miri::memory:  mark_static_initalized
TRACE:miri::memory:  Alloc 3:     function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::eval_context:  _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory:  Alloc 4:    00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory:                                                                                      └─────────(3)─────────┘ 
TRACE:miri::memory:  Alloc 3:     function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::step:  StorageDead(_3)
TRACE:miri::step:  StorageDead(_4)
TRACE:miri::step:  StorageLive(_6)
TRACE:miri::step:  StorageLive(_7)
TRACE:miri::step:  StorageLive(_8)
TRACE:miri::step:  StorageLive(_9)

This is where the fun begins. We don't have the real type info available anymore, since we did an unsize cast above.

TRACE:miri::step:  _9 = &((*_2).1: Bar)
TRACE:miri::eval_context:  _9: Undef
TRACE:miri::eval_context:  _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory:  Alloc 4:    00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory:                                                                                      └─────────(3)─────────┘ 
TRACE:miri::memory:  Alloc 3:     function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::eval_context:  _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory:  Alloc 4:    00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory:                                                                                      └─────────(3)─────────┘ 
TRACE:miri::memory:  Alloc 3:     function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::lvalue:  deref to Foo<Bar> on ByValPair(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::lvalue:  field base layout for ty Foo<Bar>: Univariant {
    variant: Struct {
        align: Align {
            raw: 49
        },
        packed: false,
        sized: false,
        offsets: [
            Size {
                raw: 0
            },
            Size {
                raw: 2
            }
        ],
        memory_index: [
            0,
            1
        ],
        min_size: Size {
            raw: 2
        }
    },
    non_zero: false
}
TRACE:miri::lvalue:  field: 1 at offset 2

see how _9 points to offset 2 in alloc 2.

TRACE:miri::eval_context:  _9: (Ptr(Pointer { alloc_id: AllocId(2), offset: 2 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory:  Alloc 4:    00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory:                                                                                      └─────────(3)─────────┘ 
TRACE:miri::memory:  Alloc 3:     function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::step:  _8 = <Bar as Bar>::get(_9) -> bb1
TRACE:miri::eval_context:  _8: Undef
TRACE:miri::eval_context:  _9: (Ptr(Pointer { alloc_id: AllocId(2), offset: 2 }), Ptr(Pointer { alloc_id: AllocId(4), offset: 0 }))
TRACE:miri::memory:  Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::memory:  Alloc 4:    00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 (32 bytes) (immutable)
TRACE:miri::memory:                                                                                      └─────────(3)─────────┘ 
TRACE:miri::memory:  Alloc 3:     function pointer: <usize as Bar>::get: fn(&usize) -> usize
TRACE:miri::terminator:  args: [
    (
        ByValPair(
            Ptr(
                Pointer {
                    alloc_id: AllocId(
                        2
                    ),
                    offset: 2
                }
            ),
            Ptr(
                Pointer {
                    alloc_id: AllocId(
                        4
                    ),
                    offset: 0
                }
            )
        ),
        &Bar
    )
]
DEBUG:miri::memory: reading fn ptr: 3
TRACE:miri::terminator:  sig: ([&usize]; variadic: false)->usize
TRACE:miri::eval_context:  load mir DefId { krate: CrateNum(0), node: DefIndex(11) => dst_field_align/4089d7c8b778d88cec885baf7b69e6df-exe::{{impl}}[0]::get[0] }
TRACE:miri::eval_context:   _1: Undef
TRACE:miri::step:   // bb0
TRACE:miri::step:   StorageLive(_2)
TRACE:miri::step:   _2 = _1
TRACE:miri::eval_context:   _2: Undef
TRACE:miri::eval_context:   _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory:   Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context:   _2: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory:   Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::step:   StorageLive(_3)
TRACE:miri::step:   _3 = (*_2)
TRACE:miri::eval_context:   _3: Undef
TRACE:miri::eval_context:   _2: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory:   Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::eval_context:   _2: Ptr(Pointer { alloc_id: AllocId(2), offset: 2 })
TRACE:miri::memory:   Alloc 2:    00 00 __ __ __ __ __ __ 0b 00 00 00 00 00 00 00 (16 bytes)
TRACE:miri::lvalue:   deref to usize on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 2 }))
TRACE:miri::memory:   allocate: 8, 8 at AllocId(5)
TRACE:miri::memory:   alignment check 2 failed for alloc AllocId(2)
error: tried to access memory with alignment 2, but alignment 8 is required
  --> tests/run-pass/dst-field-align.rs:21:30
   |
21 |     fn get(&self) -> usize { *self }
   |                              ^^^^^
   |
note: inside call to <usize as Bar>::get
  --> tests/run-pass/dst-field-align.rs:27:16
   |
27 |     assert_eq!(f.b.get(), 11);
   |                ^^^^^^^^^
note: inside call to main

Eval error with Rc

When I run the following code I get a panic.

Code:

use std::cell::RefCell;
use std::rc::Rc;

fn rc_refcell() -> i32 {
     let r = Rc::new(RefCell::new(42));
     *r.borrow_mut() += 10;
     let x = *r.borrow();
     x
}

fn main() {
    rc_refcell();
}

Backtrace:

    Finished debug [unoptimized + debuginfo] target(s) in 0.1 secs
     Running `target/debug/miri example_code.rs`
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `1`, right: `0`)', src/interpreter/mod.rs:535
stack backtrace:
   1: 0xb672ec11 - std::sys::backtrace::tracing::imp::write::h4b09e6e8c01db097
   2: 0xb673edaa - std::panicking::default_hook::{{closure}}::h1d3243f546573ff4
   3: 0xb673b949 - std::panicking::default_hook::h96c288d728df3ebf
   4: 0xb673c053 - std::panicking::rust_panic_with_hook::hb1322e5f2588b4db
   5: 0xb673bedd - std::panicking::begin_panic::hfbeda5aad583dc32
   6: 0xb673bdfe - std::panicking::begin_panic_fmt::h4fe9fb9d5109c4bf
   7: 0xb75c6794 - miri::interpreter::EvalContext::eval_rvalue_into_lvalue::h1e311a0af7ed05c7
                at /home/bjorn/Documenten/miri/src/interpreter/mod.rs:535
   8: 0xb759e79d - miri::interpreter::step::<impl miri::interpreter::EvalContext<'a, 'tcx>>::statement::he6295ec356cfef01
                at /home/bjorn/Documenten/miri/src/interpreter/step.rs:80
   9: 0xb759db8b - miri::interpreter::step::<impl miri::interpreter::EvalContext<'a, 'tcx>>::step::ha1e6c1f76ff81898
                at /home/bjorn/Documenten/miri/src/interpreter/step.rs:47
  10: 0xb75d8cfd - miri::interpreter::eval_main::h2893a72c73e2909c
                at /home/bjorn/Documenten/miri/src/interpreter/mod.rs:1171
  11: 0xb73d2369 - <miri::MiriCompilerCalls as rustc_driver::CompilerCalls<'a>>::build_controller::{{closure}}::h168a492b582a289e
                at /home/bjorn/Documenten/miri/src/bin/miri.rs:78
  12: 0xb7292b0f - rustc_driver::driver::compile_input::{{closure}}::hdf708031c67974d4
  13: 0xb728287b - rustc_driver::driver::phase_3_run_analysis_passes::{{closure}}::hcac3a2328cd9259a
  14: 0xb71c5f3c - rustc::ty::context::TyCtxt::create_and_enter::h391a9cd3e27a0125
  15: 0xb7243411 - rustc_driver::driver::compile_input::hc0edbed7edb3eb18
  16: 0xb72693ff - rustc_driver::run_compiler::h22d678d32fb7c300
  17: 0xb73d09ce - miri::main::he47b4050ae2aa0ba
                at /home/bjorn/Documenten/miri/src/bin/miri.rs:141
  18: 0xb673bca5 - std::panicking::try::do_call::h8a9d45772be711b8
  19: 0xb67468e7 - __rust_maybe_catch_panic
  20: 0xb673aee6 - std::rt::lang_start::haaae1186de9de8cb
  21: 0xb73d253a - main
  22: 0xb6495af2 - __libc_start_main
  23: 0xb736c140 - <unknown>
error: Process didn't exit successfully: `target/debug/miri example_code.rs` (exit code: 101)

ByValPair <-> ByVal transmutes can cause Miri to panic

ByValPair -> ByVal

Source

#![feature(i128_type)]

fn main() {
    let bad = unsafe {
        std::mem::transmute::<&[u8], u128>(&[1u8]);
        // On 32-bit: transmute::<&[u8], u64>(&[1u8]);
    };
    bad + 1;
}

Output and error

TRACE:miri::step 0  _1 = std::intrinsics::transmute::<&[u8], u128>(_2) -> bb1
TRACE:miri::eval_context 0  frame[0] _1: Undef
TRACE:miri::eval_context 0  frame[0] _2: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0  Alloc 2:    01 (1 bytes) (immutable)
TRACE:miri::step 0  // bb1
TRACE:miri::step 0  StorageDead(_2)
TRACE:miri::step 0  StorageDead(_3)
TRACE:miri::step 0  StorageDead(_4)
TRACE:miri::step 0  StorageLive(_6)
TRACE:miri::step 0  _6 = _1
TRACE:miri::eval_context 0  frame[0] _6: Undef
TRACE:miri::eval_context 0  frame[0] _1: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0  Alloc 2:    01 (1 bytes) (immutable)
TRACE:miri::eval_context 0  frame[0] _6: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0  Alloc 2:    01 (1 bytes) (immutable)
TRACE:miri::step 0  _7 = CheckedAdd(_6, const 1u128)
TRACE:miri::eval_context 0  frame[0] _7: Undef
TRACE:miri::eval_context 0  frame[0] _6: (Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }), Bytes(1))
TRACE:miri::memory 0  Alloc 2:    01 (1 bytes) (immutable)
error: internal compiler error: src/eval_context.rs:903: value_to_primval can't work with fat pointers

ByVal -> ByValPair

Source

#![feature(i128_type)]

fn main() {
    let bad = unsafe {
        std::mem::transmute::<u128, &[u8]>(42);
        // On 32-bit: transmute::<u64, &[u8]>(42);
    };
    bad[0];
}

Output and error

TRACE:miri::step 0  _1 = std::intrinsics::transmute::<u128, &[u8]>(const 42u128) -> bb1
TRACE:miri::eval_context 0  frame[0] _1: Undef
TRACE:miri::step 0  // bb1
TRACE:miri::step 0  StorageLive(_3)
TRACE:miri::step 0  _4 = Len((*_1))
TRACE:miri::eval_context 0  frame[0] _4: Undef
TRACE:miri::eval_context 0  frame[0] _1: Bytes(42)
TRACE:miri::eval_context 0  frame[0] _1: Bytes(42)
TRACE:miri::lvalue 0  deref to [u8] on ByVal(Bytes(42))
thread 'main' panicked at 'not yet implemented', src/value.rs:116

Running miri on the run-fail test suite of rustc

Command

time MIRI_RUSTC_TEST=../rust/src/test/run-fail cargo test --release 2> cargo_test_output_fail.txt

Output

Note that "success" is bad 🤣 . Since it means that a run-fail tests passed. Search for "... ok" in the full output to figure out which run-fail tests need a fix in miri.

10 success, 15 no mir, 0 crate not found, 95 failed, 0 C fn, 0 ABI, 4 unsupported, 0 intrinsic
0 success, 15 no mir, 0 crate not found, 105 failed, 0 C fn, 0 ABI, 4 unsupported, 0 intrinsic

The "other reasons" errors

(sorted, deduplicated)

  74 the evaluated program panicked
   6 main function not found
   1 index out of bounds: the len is 5 but the index is 10 at ../rust/src/test/run-fail/mir_indexing_oob_3.rs:16:5: 16:10
   1 index out of bounds: the len is 5 but the index is 10 at ../rust/src/test/run-fail/mir_indexing_oob_2.rs:16:5: 16:10
   1 index out of bounds: the len is 5 but the index is 10 at ../rust/src/test/run-fail/mir_indexing_oob_1.rs:16:5: 16:10
   1 index out of bounds: the len is 3 but the index is 3 at ../rust/src/test/run-fail/dst-raw-slice.rs:17:18: 17:25
   1 index out of bounds: the len is 3 but the index is 2305843009213693952 at /checkout/src/libcore/slice.rs:658:10: 658:24
   1 index out of bounds: the len is 1 but the index is 2 at /checkout/src/libcollections/vec.rs:1422:10: 1422:25
   1 a raw memory access tried to access part of a pointer value as raw bytes
   1 RemainderByZero at ../rust/src/test/run-fail/mod-zero.rs:15:14: 15:19
   1 Overflow(Sub) at ../rust/src/test/run-fail/overflowing-sub.rs:15:14: 15:31
   1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-6.rs:18:14: 18:29
   1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-5.rs:17:14: 17:29
   1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-4.rs:21:13: 21:23
   1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-3.rs:17:14: 17:26
   1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-2.rs:17:14: 17:26
   1 Overflow(Shr) at ../rust/src/test/run-fail/overflowing-rsh-1.rs:17:14: 17:26
   1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-4.rs:21:13: 21:23
   1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-3.rs:17:14: 17:25
   1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-2.rs:17:14: 17:21
   1 Overflow(Shl) at ../rust/src/test/run-fail/overflowing-lsh-1.rs:17:14: 17:25
   1 Overflow(Neg) at ../rust/src/test/run-fail/overflowing-neg.rs:15:14: 15:27
   1 Overflow(Mul) at /checkout/src/libcore/num/mod.rs:2220:24: 2220:35
   1 Overflow(Mul) at /checkout/src/libcore/num/mod.rs:1168:24: 1168:35
   1 Overflow(Mul) at ../rust/src/test/run-fail/overflowing-mul.rs:15:13: 15:22
   1 Overflow(Add) at ../rust/src/test/run-fail/overflowing-add.rs:15:14: 15:27
   1 DivisionByZero at ../rust/src/test/run-fail/divide-by-zero.rs:15:14: 15:19

can't call C ABI function

unsupported ABI

unsupported

   3 threading
   1 program arguments

unimplemented intrinsics

mir not found

   6 <std::string::String as std::convert::From<&'a str>>::from
   4 std::io::stderr
   2 std::panic::set_hook
   1 std::panic::take_hook
   1 std::fmt::format
   1 std::collections::hash_map::RandomState::new::KEYS

crate not found

Full Output

cargo_test_output_fail.txt

fast mode and tracing mode

Original post: add tail-call optimization

<scott> a call that branches to the return block on success could be TCO'd


Things to add in super fast mode:

  • JIT compilation of MIR

Things to add in fast mode:

  • tail-call optimization

Things to add in trace mode:

  • type information of Allocations
  • taint analysis
  • memory leak analysis

bad substs for tuple-like constructor function pointer

Source

fn main() {
    let f: fn(i32) -> Option<i32> = Some::<i32>;
    f(42);
}

Output and error

TRACE:miri::step:  _1 = std::option::Option<i32>::Some as fn(i32) -> std::option::Option<i32> (ReifyFnPointer)
TRACE:miri::eval_context:  _1: Undef
DEBUG:miri::memory: creating fn ptr: 2
TRACE:miri::eval_context:  _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:     function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
TRACE:miri::step:  StorageLive(_3)
TRACE:miri::step:  _3 = _1
TRACE:miri::eval_context:  _3: Undef
TRACE:miri::eval_context:  _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:     function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
TRACE:miri::eval_context:  _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:     function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
TRACE:miri::step:  _2 = _3(const 42i32) -> bb1
TRACE:miri::eval_context:  _2: Undef
TRACE:miri::eval_context:  _3: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:  Alloc 2:     function pointer: std::prelude::v1::Some: fn(i32) -> std::option::Option<i32>
DEBUG:miri::memory: reading fn ptr: 2
error: internal compiler error: /buildslave/rust-buildbot/slave/nightly-dist-rustc-linux/build/src/librustc/ty/subst.rs:476: Type parameter `T/#0` (T/0) out of range when substituting (root type=Some(std::option::Option<T>)) substs=[]

Add configurable resource limits

Eventually, we want rustc to be able to limit the amount of time spent evaluating a constant value, since Miri const eval could have infinite loops or simply very long calculations.

Thanks to the EvalContext::step interface, this should be pretty easy. Likely, we can just have a step count limit which will be configurable like rustc's recursion_depth.

&A as *const _ == &A as *const _ not guaranteed for `const A`

miri creates two allocations for the *b"hi" in A.
see MIR in https://is.gd/bCUD7p
This makes sense, since there's _1 = A; _2 = &_1 instead of _2 = &A.

Should this

a) be fixed in rustc, or
b) should we cache all literals (we have FIXMEs for that), or
c) should we add some Cow-like structure to lvalues, so they can refer to other lvalues "ByVal"?
d) is the test ( https://github.com/rust-lang/rust/blob/master/src/test/run-pass/const-str-ptr.rs ) wrong?

@eddyb: any preferences?

Found more evidence for b and against d:

#![feature(const_fn)]

const fn foo() -> *const i8 {
    b"foo" as *const _ as *const i8
}

const fn bar() -> i32 {
    *&{(1, 2, 3).1}
}

fn main() {
    assert_eq!(foo(), b"foo" as *const _ as *const i8);
    assert_eq!(bar(), 2);
}

`Memory` should use the target data layout information from rustc

Memory currently has a pointer_size field. Instead, it should take tcx.data_layout to get not only pointer size, but also endianness and alignment for all primitive types. Then, Miri should diagnose unaligned memory accesses and read/write all multi-byte values with the given target endianness rather than host endianness.

  • Use tcx.data_layout in Memory.
  • Use target endianness.
  • Detect unaligned memory accesses.

only the outer most value is frozen in constants

so I think the following piece of code works, while it most definitely shouldn't, haven't tested yet, but am on it.

let x = &1; // the `&1` is promoted to a constant, but only the pointer is frozen, not the pointee
let y = unsafe { &mut *(x as *const i32 as *mut i32) };
let *y = 42;
assert_eq!(*x, 42);

There's no mir in My Mir

Compiling miri v0.1.0 (file:///duh/miri)
src/interpreter.rs:1008:23: 1008:30 error: bitshift exceeds the type's number of bits, #[deny(exceeding_bitshifts)] on by default
src/interpreter.rs:1008             n if n <= 1 << 32 => 4,
                                              ^~~~~~~
error: aborting due to previous error

Function pointers need to be checked against their original types

The following code demonstrates the problem:

fn cast_fn_ptr() {
    fn f() {}

    let g = unsafe {
        transmute::<fn(), fn(i32)>(f)
    };

    // Here Miri blindly lets us call `f` as if it took an argument.
    // This causes Miri to go out bounds on the arguments `Vec`
    // (see backtrace below).
    g(42)
}

Logs and backtrace:

DEBUG:miri 0 Interpreting: cast_fn_ptr
TRACE:miri::interpreter::stepper 0  tmp0 = cast_fn_ptr::f as fn() (ReifyFnPointer)
DEBUG:miri::memory 0  creating fn ptr: 4
TRACE:miri::interpreter::stepper 0  var0 = std::mem::transmute::<fn(), fn(i32)>(tmp0) -> bb1
TRACE:miri::interpreter::stepper 0  // bb1
TRACE:miri::interpreter::stepper 0  tmp1 = var0
TRACE:miri::interpreter::stepper 0  return = tmp1(const 42i32) -> bb2
DEBUG:miri::memory 0  reading fn ptr: 4
thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0', ../src/libcollections/vec.rs:1167
stack backtrace:
   1:     0x7f9c6bc0561f - std::sys::backtrace::tracing::imp::write::h6528da8103c51ab9
   2:     0x7f9c6bc1324b - std::panicking::default_hook::_$u7b$$u7b$closure$u7d$$u7d$::hbe741a5cc3c49508
   3:     0x7f9c6bc12e7f - std::panicking::default_hook::he0146e6a74621cb4
   4:     0x7f9c6bbd916e - std::panicking::rust_panic_with_hook::h983af77c1a2e581b
   5:     0x7f9c6bc13491 - std::panicking::begin_panic::he426e15a3766089a
   6:     0x7f9c6bbdb1ca - std::panicking::begin_panic_fmt::hdddb415186c241e7
   7:     0x7f9c6bc1342e - rust_begin_unwind
   8:     0x7f9c6bc5c4cf - core::panicking::panic_fmt::hf4e16cb7f0d41a25
   9:     0x7f9c6bc5c6b2 - core::panicking::panic_bounds_check::h14f942e6ac026712
  10:     0x56106d71c38f - _<collections..vec..Vec<T> as core..ops..Index<usize>>::index::h21bed189d0a022e0
                        at ../src/libcollections/vec.rs:1167
  11:     0x56106d711465 - miri::interpreter::EvalContext::eval_fn_call::h0ffe2222a9193951
                        at src/interpreter/mod.rs:551
  12:     0x56106d70a974 - miri::interpreter::EvalContext::eval_terminator::h906ffc9b7c429822
                        at src/interpreter/mod.rs:438
  13:     0x56106d73bbf6 - miri::interpreter::stepper::Stepper::terminator::he5b0f785cd245fcf
                        at src/interpreter/stepper.rs:38
  14:     0x56106d6a7354 - miri::interpreter::stepper::Stepper::step::hc3a72975df25ed8c
                        at src/interpreter/stepper.rs:85
  15:     0x56106d6a68ed - miri::interpreter::step::h5d73db301ea80161
                        at src/interpreter/mod.rs:29
  16:     0x56106d65973e - miri::interpret_start_points::hdb489d38e2bc843c
                        at src/bin/miri.rs:68
  17:     0x56106d658ff4 - _<miri..MiriCompilerCalls as rustc_driver..CompilerCalls<'a>>::build_controller::_$u7b$$u7b$closure$u7d$$u7d$::h44743d57da4e97a6
                        at src/bin/miri.rs:37
  18:     0x7f9c6ddf974d - rustc_driver::driver::compile_input::_$u7b$$u7b$closure$u7d$$u7d$::hf187cb470aad2bf2
  19:     0x7f9c6ddf616d - rustc_driver::driver::phase_3_run_analysis_passes::_$u7b$$u7b$closure$u7d$$u7d$::hd565ad56c5876a16
  20:     0x7f9c6ddef939 - rustc::ty::context::TyCtxt::create_and_enter::habef58c7230d34f9
  21:     0x7f9c6ddb9dff - rustc_driver::driver::compile_input::hfd60b020f6d0208d
  22:     0x7f9c6dda5d24 - rustc_driver::run_compiler::h884d01d12eb76bbb
  23:     0x56106d6704b1 - miri::main::h496a9d1b73279651
                        at src/bin/miri.rs:119
  24:     0x7f9c6bc127d8 - std::panicking::try::call::h852b0d5f2eec25e4
  25:     0x7f9c6bc2170b - __rust_try
  26:     0x7f9c6bc216ae - __rust_maybe_catch_panic
  27:     0x7f9c6bc1227e - std::rt::lang_start::hfe4efe1fc39e4a30
  28:     0x56106d68d9e9 - main
  29:     0x7f9c6b332abf - __libc_start_main
  30:     0x56106d658c58 - _start
  31:                0x0 - <unknown>

fn pointer as closure trait object "bad VtableFnPointer fn_ty: fn()"

Source

fn foo() {}

fn main() {
    let f: &Fn() = &(foo as fn());
    f();
}

Output and error

TRACE:miri::step 0  _1 = _2 as &std::ops::Fn() (Unsize)
TRACE:miri::eval_context 0  frame[0] _1: Undef
TRACE:miri::eval_context 0  frame[0] _2: Ptr(Pointer { alloc_id: AllocId(3), offset: 0 })
TRACE:miri::memory 0  Alloc 3:    00 00 00 00 00 00 00 00 (8 bytes) (immutable)
TRACE:miri::memory 0              └─────────(2)─────────┘
TRACE:miri::memory 0  Alloc 2:     function pointer
DEBUG:miri::vtable: get_vtable(trait_ref=Binder(<fn() as std::ops::Fn<()>>))
error: internal compiler error: src/vtable.rs:63: bad VtableFnPointer fn_ty: fn()       

refactor vtables

I think it would make sense to store vtables not as allocations but as a

struct Vtable {
    drop: Option<Pointer>,
    size: u64,
    align: u64,
    methods: Vec<Pointer>,
}

Disadavantages

  • This would forbid users from writing their own vtables
  • This would forbid users from accessing the vtable as memory

Advantages

  • Stop accessing arbitrary offsets in the vtable depending on which value we want and use struct fields instead
  • Give nice error messages when a vtable ptr is mis-used

running miri on rustc's test suite (run-pass)

output of

time MIRI_SYSROOT=~/.xargo/HOST MIRI_RUSTC_TEST=../rust/src/test/run-pass cargo run --release --manifest-path="rustc_tests/Cargo.toml" 2> cargo_test_output.txt > /dev/null

1617 success, 558 no mir, 205 crate not found, 158 failed
1630 success, 560 no mir, 205 crate not found, 143 failed
1632 success, 561 no mir, 205 crate not found, 140 failed
1632 success, 562 no mir, 205 crate not found, 139 failed
1633 success, 563 no mir, 205 crate not found, 137 failed
1638 success, 554 no mir, 205 crate not found, 141 failed
1864 success, 211 no mir, 205 crate not found, 258 failed
1867 success, 202 no mir, 205 crate not found, 264 failed
1868 success, 202 no mir, 205 crate not found, 94 failed, 43 C fn, 9 ABI, 102 unsupported, 5 intrinsic
1878 success, 201 no mir, 205 crate not found, 58 failed, 42 C fn, 9 ABI, 121 unsupported, 5 intrinsic
1883 success, 201 no mir, 205 crate not found, 55 failed, 42 C fn, 9 ABI, 121 unsupported, 6 intrinsic
1885 success, 201 no mir, 205 crate not found, 53 failed, 42 C fn, 9 ABI, 121 unsupported, 6 intrinsic
1886 success, 201 no mir, 205 crate not found, 52 failed, 42 C fn, 9 ABI, 121 unsupported, 6 intrinsic
1902 success, 202 no mir, 206 crate not found, 60 failed, 44 C fn, 0 ABI, 122 unsupported, 6 intrinsic
1901 success, 202 no mir, 206 crate not found, 54 failed, 44 C fn, 9 ABI, 122 unsupported, 6 intrinsic
1904 success, 202 no mir, 206 crate not found, 49 failed, 44 C fn, 9 ABI, 122 unsupported, 6 intrinsic
1905 success, 202 no mir, 206 crate not found, 52 failed, 44 C fn, 9 ABI, 122 unsupported, 2 intrinsic
1907 success, 202 no mir, 206 crate not found, 52 failed, 44 C fn, 9 ABI, 122 unsupported, 0 intrinsic
1913 success, 202 no mir, 206 crate not found, 50 failed, 44 C fn, 9 ABI, 122 unsupported, 0 intrinsic
1933 success, 201 no mir, 209 crate not found, 49 failed, 38 C fn, 0 ABI, 122 unsupported, 6 intrinsic
1985 success, 206 no mir, 219 crate not found, 62 failed, 40 C fn, 0 ABI, 122 unsupported, 6 intrinsic
2016 success, 208 no mir, 225 crate not found, 57 failed, 34 C fn, 0 ABI, 123 unsupported, 10 intrinsic
2022 success, 208 no mir, 225 crate not found, 51 failed, 34 C fn, 0 ABI, 123 unsupported, 10 intrinsic
2025 success, 208 no mir, 225 crate not found, 48 failed, 34 C fn, 0 ABI, 123 unsupported, 10 intrinsic
2179 success, 2 no mir, 235 crate not found, 68 failed, 145 C fn, 0 ABI, 15 unsupported, 6 intrinsic
2179 success, 3 no mir, 235 crate not found, 73 failed, 42 C fn, 0 ABI, 112 unsupported, 6 intrinsic
2188 success, 14 no mir, 236 crate not found, 79 failed, 44 C fn, 0 ABI, 113 unsupported, 6 intrinsic
2457 success, 5 no mir, 252 crate not found, 100 failed, 44 C fn, 0 ABI, 118 unsupported, 14 intrinsic

Tracking the state in gist from now on, so we can see diffs

https://gist.github.com/oli-obk/5a0832eef3124ad9088748fc9e759318

handle packed structs

currently we can't handle packed structs with fields whose type has an alignment bigger than 1 (because packed structs have an alignment of 1). So accessing a packed struct's field will do the alignment check and fail.

Implement the char primitive type.

This has three components:

  • Handle char literal constants.
  • Add a char variant to PrimVal.
  • Diagnose reads and writes of invalid char values in Memory.

`static mut`s deallocate their sub-allocations, making their pointers dangle

Source

static mut FOO: &'static i32 = &42i32;

pub fn main() {
    unsafe { *FOO; }
}

Output and error

TRACE:miri::step:  pushing stack frame for global: FOO
TRACE:miri::step:   _2 = const 42i32
TRACE:miri::eval_context:   _2: Undef
TRACE:miri::eval_context:   _2: Bytes(42)
TRACE:miri::step:   _1 = &_2
TRACE:miri::eval_context:   _1: Undef
TRACE:miri::eval_context:   _2: Bytes(42)
TRACE:miri::eval_context:   _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:   Alloc 2:    2a 00 00 00 (4 bytes)
TRACE:miri::step:   _0 = &(*_1)
TRACE:miri::eval_context:   _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:   Alloc 2:    2a 00 00 00 (4 bytes)
TRACE:miri::eval_context:   _1: Ptr(Pointer { alloc_id: AllocId(2), offset: 0 })
TRACE:miri::memory:   Alloc 2:    2a 00 00 00 (4 bytes)
TRACE:miri::lvalue:   deref to i32 on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }))
TRACE:miri::step:   return
TRACE:miri::eval_context:  deallocating local
TRACE:miri::memory:  Alloc 2:    2a 00 00 00 (4 bytes)
DEBUG:miri::memory: deallocated : 2
TRACE:miri::step:  // bb0
TRACE:miri::step:  _2 = (*FOO)
TRACE:miri::eval_context:  _2: Undef
TRACE:miri::lvalue:  deref to i32 on ByVal(Ptr(Pointer { alloc_id: AllocId(2), offset: 0 }))
error: dangling pointer was dereferenced
 --> const-vec-of-fns.rs:4:14
  |
4 |     unsafe { *FOO; }
  |              ^^^^

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.