Skip to content

Commit

Permalink
Auto merge of #1485 - RalfJung:miri-extern-fn, r=oli-obk
Browse files Browse the repository at this point in the history
 Miri: use extern fn to expose interpreter operations to program; fix leak checker on Windows

This is the Miri side of rust-lang/rust#74681.

Fixes #1302
Fixes #1318
  • Loading branch information
bors committed Jul 25, 2020
2 parents 592b140 + c641fbd commit 91b58c9
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 15 deletions.
23 changes: 23 additions & 0 deletions README.md
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
@@ -1 +1 @@
4825e12fc9c79954aa0fe18f5521efa6c19c7539
0e11fc8053d32c44e7152865852acc5c3c54efb3
8 changes: 5 additions & 3 deletions src/eval.rs
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
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
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
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
@@ -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
@@ -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

0 comments on commit 91b58c9

Please sign in to comment.