Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miri: use extern fn to expose interpreter operations to program; fix leak checker on Windows #1485

Merged
merged 5 commits into from
Jul 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,29 @@ different Miri binaries, and as such worth documenting:
interpret the code but compile it like rustc would. This is useful to be sure
that the compiled `rlib`s are compatible with Miri.

## Miri `extern` functions

Miri provides some `extern` functions that programs can import to access
Miri-specific functionality:

```rust
#[cfg(miri)]
extern "Rust" {
/// Miri-provided extern function to mark the block `ptr` points to as a "root"
/// for some static memory. This memory and everything reachable by it is not
/// considered leaking even if it still exists when the program terminates.
///
/// `ptr` has to point to the beginning of an allocated block.
fn miri_static_root(ptr: *const u8);

/// Miri-provided extern function to begin unwinding with the given payload.
///
/// This is internal and unstable and should not be used; we give it here
/// just to be complete.
fn miri_start_panic(payload: *mut u8) -> !;
}
```

## Contributing and getting help

If you want to contribute to Miri, great! Please check out our
Expand Down
2 changes: 1 addition & 1 deletion rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4825e12fc9c79954aa0fe18f5521efa6c19c7539
0e11fc8053d32c44e7152865852acc5c3c54efb3
8 changes: 5 additions & 3 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::ffi::OsStr;

use rand::rngs::StdRng;
use rand::SeedableRng;
use log::info;

use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, layout::LayoutCx, TyCtxt};
Expand Down Expand Up @@ -195,8 +196,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
/// Returns `Some(return_code)` if program executed completed.
/// Returns `None` if an evaluation error occured.
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
// FIXME: on Windows, we ignore leaks (https://github.com/rust-lang/miri/issues/1302).
let ignore_leaks = config.ignore_leaks || tcx.sess.target.target.target_os == "windows";
// Copy setting before we move `config`.
let ignore_leaks = config.ignore_leaks;

let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
Ok(v) => v,
Expand Down Expand Up @@ -244,7 +245,8 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
match res {
Ok(return_code) => {
if !ignore_leaks {
let leaks = ecx.memory.leak_report();
info!("Additonal static roots: {:?}", ecx.machine.static_roots);
let leaks = ecx.memory.leak_report(&ecx.machine.static_roots);
if leaks != 0 {
tcx.sess.err("the evaluated program leaked memory");
// Ignore the provided return code - let the reported error
Expand Down
4 changes: 4 additions & 0 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ pub struct Evaluator<'mir, 'tcx> {

/// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
pub(crate) layouts: PrimitiveLayouts<'tcx>,

/// Allocations that are considered roots of static memory (that may leak).
pub(crate) static_roots: Vec<AllocId>,
}

impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
Expand Down Expand Up @@ -289,6 +292,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
time_anchor: Instant::now(),
layouts,
threads: ThreadManager::default(),
static_roots: Vec::new(),
}
}
}
Expand Down
17 changes: 16 additions & 1 deletion src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
def_id: DefId,
args: &[OpTy<'tcx, Tag>],
ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
_unwind: Option<mir::BasicBlock>,
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
let this = self.eval_context_mut();
let attrs = this.tcx.get_attrs(def_id);
Expand All @@ -126,6 +126,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// First: functions that diverge.
let (dest, ret) = match ret {
None => match link_name {
"miri_start_panic" => {
this.handle_miri_start_panic(args, unwind)?;
return Ok(None);
}
// This matches calls to the foreign item `panic_impl`.
// The implementation is provided by the function with the `#[panic_handler]` attribute.
"panic_impl" => {
Expand Down Expand Up @@ -193,6 +197,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
// Here we dispatch all the shims for foreign functions. If you have a platform specific
// shim, add it to the corresponding submodule.
match link_name {
// Miri-specific extern functions
"miri_static_root" => {
let &[ptr] = check_arg_count(args)?;
let ptr = this.read_scalar(ptr)?.not_undef()?;
let ptr = this.force_ptr(ptr)?;
if ptr.offset != Size::ZERO {
throw_unsup_format!("pointer passed to miri_static_root must point to beginning of an allocated block");
}
this.machine.static_roots.push(ptr.alloc_id);
}

// Standard C allocation
"malloc" => {
let &[size] = check_arg_count(args)?;
Expand Down
9 changes: 3 additions & 6 deletions src/shims/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
instance: ty::Instance<'tcx>,
args: &[OpTy<'tcx, Tag>],
ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
unwind: Option<mir::BasicBlock>,
_unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let intrinsic_name = this.tcx.item_name(instance.def_id());
Expand All @@ -32,13 +32,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
return Ok(());
}

// First handle intrinsics without return place.
// All supported intrinsics have a return place.
let intrinsic_name = &*intrinsic_name.as_str();
let (dest, ret) = match ret {
None => match intrinsic_name {
"miri_start_panic" => return this.handle_miri_start_panic(args, unwind),
_ => throw_unsup_format!("unimplemented (diverging) intrinsic: {}", intrinsic_name),
},
None => throw_unsup_format!("unimplemented (diverging) intrinsic: {}", intrinsic_name),
Some(p) => p,
};

Expand Down
2 changes: 0 additions & 2 deletions tests/compile-fail/memleak.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore-windows: We do not check leaks on Windows

//error-pattern: the evaluated program leaked memory

fn main() {
Expand Down
2 changes: 0 additions & 2 deletions tests/compile-fail/memleak_rc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ignore-windows: We do not check leaks on Windows

//error-pattern: the evaluated program leaked memory

use std::rc::Rc;
Expand Down