Skip to content

Commit

Permalink
Implement non-deterministc mode
Browse files Browse the repository at this point in the history
Part of #653

This allows us to properly implement getrandom(),
which unlocks the default HashMap type (e.g. HashMap<K, V>)
with RandomState)

This commit adds a new '-Zmiri-seed=<seed>' option. When present,
this option takes a 64-bit hex value, which is used as the seed
to an internal PRNG. This PRNG is used to implement the 'getrandom()'
syscall.

When '-Zmiri-seed' is not passed, 'getrandom()' will be disabled.
  • Loading branch information
Aaron1011 committed Apr 7, 2019
1 parent 6871145 commit cb1a3cc
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 10 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ shell-escape = "0.1.4"
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
# for more information.
rustc-workspace-hack = "1.0.0"
hex = "0.3.2"
rand = "0.6.5"

[build-dependencies]
vergen = "3"
Expand Down
20 changes: 18 additions & 2 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ fn main() {

// Parse our arguments and split them across `rustc` and `miri`.
let mut validate = true;
let mut seed: Option<u64> = None;
let mut rustc_args = vec![];
let mut miri_args = vec![];
let mut after_dashdash = false;
Expand All @@ -146,7 +147,22 @@ fn main() {
after_dashdash = true;
}
_ => {
rustc_args.push(arg);
let split: Vec<String> = arg.split("-Zmiri-seed=").map(|s| s.to_owned()).collect();
if split.len() == 2 {
if seed.is_some() {
panic!("Cannot specify -Zmiri-seed multiple times!");
}
let seed_raw = hex::decode(&split[1]).unwrap();
if seed_raw.len() > 8 {
panic!(format!("-Zmiri-seed must be at most 8 bytes, was {}", seed_raw.len()));
}

let mut bytes = [0; 8];
bytes[..seed_raw.len()].copy_from_slice(&hex::decode(&split[1]).unwrap());
seed = Some(u64::from_be_bytes(bytes));
} else {
rustc_args.push(arg);
}
}
}
}
Expand All @@ -163,7 +179,7 @@ fn main() {

debug!("rustc arguments: {:?}", rustc_args);
debug!("miri arguments: {:?}", miri_args);
let miri_config = miri::MiriConfig { validate, args: miri_args };
let miri_config = miri::MiriConfig { validate, args: miri_args, seed };
let result = rustc_driver::report_ices_to_stderr_if_any(move || {
rustc_driver::run_compiler(&rustc_args, &mut MiriCompilerCalls { miri_config }, None, None)
}).and_then(|result| result);
Expand Down
31 changes: 28 additions & 3 deletions src/fn_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use rustc::hir::def_id::DefId;
use rustc::mir;
use syntax::attr;

use rand::RngCore;

use crate::*;

impl<'a, 'mir, 'tcx> EvalContextExt<'a, 'mir, 'tcx> for crate::MiriEvalContext<'a, 'mir, 'tcx> {}
Expand Down Expand Up @@ -216,9 +218,32 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
// is called if a `HashMap` is created the regular way.
match this.read_scalar(args[0])?.to_usize(this)? {
318 | 511 => {
return err!(Unimplemented(
"miri does not support random number generators".to_owned(),
))
match this.machine.rng.as_ref() {
Some(rng) => {
let ptr = this.read_scalar(args[1])?.to_ptr()?;
let len = this.read_scalar(args[2])?.to_usize(this)?;

// The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
// neither of which have any effect on our current PRNG
let _flags = this.read_scalar(args[3])?.to_i32()?;

let mut data = vec![0; len as usize];
rng.borrow_mut().fill_bytes(&mut data);

this.memory_mut().get_mut(ptr.alloc_id)?
.write_bytes(tcx, ptr, &data)?;

this.write_scalar(Scalar::from_uint(len, dest.layout.size), dest)?;

},
None => {
return err!(Unimplemented(
"miri does not support random number generators in deterministic mode!
Use '-Zmiri-seed=<seed>' to enable random number generation".to_owned(),
))
}
}

}
id => {
return err!(Unimplemented(
Expand Down
16 changes: 14 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ mod stacked_borrows;

use std::collections::HashMap;
use std::borrow::Cow;
use std::cell::RefCell;

use rand::rngs::StdRng;
use rand::SeedableRng;

use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
use rustc::ty::layout::{LayoutOf, Size, Align};
Expand Down Expand Up @@ -60,6 +64,9 @@ pub fn miri_default_args() -> &'static [&'static str] {
pub struct MiriConfig {
pub validate: bool,
pub args: Vec<String>,

// The seed to use when non-determinism is required (e.g. getrandom())
pub seed: Option<u64>
}

// Used by priroda.
Expand All @@ -71,7 +78,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
let mut ecx = InterpretCx::new(
tcx.at(syntax::source_map::DUMMY_SP),
ty::ParamEnv::reveal_all(),
Evaluator::new(config.validate),
Evaluator::new(config.validate, config.seed),
);

let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
Expand Down Expand Up @@ -326,10 +333,14 @@ pub struct Evaluator<'tcx> {

/// Stacked Borrows state.
pub(crate) stacked_borrows: stacked_borrows::State,

/// The random number generator to use if Miri
/// is running in non-deterministic mode
pub(crate) rng: Option<RefCell<StdRng>>
}

impl<'tcx> Evaluator<'tcx> {
fn new(validate: bool) -> Self {
fn new(validate: bool, seed: Option<u64>) -> Self {
Evaluator {
env_vars: HashMap::default(),
argc: None,
Expand All @@ -339,6 +350,7 @@ impl<'tcx> Evaluator<'tcx> {
tls: TlsData::default(),
validate,
stacked_borrows: stacked_borrows::State::default(),
rng: seed.map(|s| RefCell::new(StdRng::seed_from_u64(s)))
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions tests/compile-fail/getrandom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#![feature(rustc_private)]
extern crate libc;

fn main() {
let mut buf = [0u8; 5];
unsafe {
libc::syscall(libc::SYS_getrandom, &mut buf as &mut [u8] as *mut [u8] as *mut u8 as *mut libc::c_void, 5, 0);
//~^ ERROR constant evaluation error: miri does not support random number generators in deterministic mode!
}
}
16 changes: 13 additions & 3 deletions tests/run-pass/hashmap.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// compile-flags: -Zmiri-seed=0000000000000000

use std::collections::{self, HashMap};
use std::hash::BuildHasherDefault;
use std::hash::{BuildHasherDefault, BuildHasher};

fn main() {
let mut map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = Default::default();
fn test_map<S: BuildHasher>(mut map: HashMap<i32, i32, S>) {
map.insert(0, 0);
assert_eq!(map.values().fold(0, |x, y| x+y), 0);

Expand All @@ -22,4 +23,13 @@ fn main() {
assert_eq!(map.values().fold(0, |x, y| x+y), num*(num-1)/2);

// TODO: Test Entry API, Iterators, ...

}

fn main() {
let map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = Default::default();
let map_normal: HashMap<i32, i32> = HashMap::new();

test_map(map);
test_map(map_normal);
}

0 comments on commit cb1a3cc

Please sign in to comment.