diff --git a/Cargo.toml b/Cargo.toml index d580d4b8ec..6c77315bea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/bin/miri-rustc-tests.rs b/src/bin/miri-rustc-tests.rs index e54be4644f..ce2ad1a271 100644 --- a/src/bin/miri-rustc-tests.rs +++ b/src/bin/miri-rustc-tests.rs @@ -48,7 +48,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { fn visit_item(&mut self, i: &'hir hir::Item) { if let hir::ItemKind::Fn(.., body_id) = i.node { if i.attrs.iter().any(|attr| attr.check_name("test")) { - let config = MiriConfig { validate: true, args: vec![] }; + let config = MiriConfig { validate: true, args: vec![], seed: None }; let did = self.0.hir().body_owner_def_id(body_id); println!("running test: {}", self.0.def_path_debug_str(did)); miri::eval_main(self.0, did, config); @@ -61,7 +61,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { } tcx.hir().krate().visit_all_item_likes(&mut Visitor(tcx)); } else if let Some((entry_def_id, _)) = tcx.entry_fn(LOCAL_CRATE) { - let config = MiriConfig { validate: true, args: vec![] }; + let config = MiriConfig { validate: true, args: vec![], seed: None }; miri::eval_main(tcx, entry_def_id, config); compiler.session().abort_if_errors(); diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 4cc0d955d3..7f15d00e2c 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -126,6 +126,7 @@ fn main() { // Parse our arguments and split them across `rustc` and `miri`. let mut validate = true; + let mut seed: Option = None; let mut rustc_args = vec![]; let mut miri_args = vec![]; let mut after_dashdash = false; @@ -146,7 +147,22 @@ fn main() { after_dashdash = true; } _ => { - rustc_args.push(arg); + let split: Vec = 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); + } } } } @@ -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); diff --git a/src/fn_call.rs b/src/fn_call.rs index 038f5ed8a0..ba610e8b23 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -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> {} @@ -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=' to enable random number generation".to_owned(), + )) + } + } + } id => { return err!(Unimplemented( diff --git a/src/lib.rs b/src/lib.rs index be58c6a6a4..9b4b69b6d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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}; @@ -60,6 +64,9 @@ pub fn miri_default_args() -> &'static [&'static str] { pub struct MiriConfig { pub validate: bool, pub args: Vec, + + // The seed to use when non-determinism is required (e.g. getrandom()) + pub seed: Option } // Used by priroda. @@ -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); @@ -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> } impl<'tcx> Evaluator<'tcx> { - fn new(validate: bool) -> Self { + fn new(validate: bool, seed: Option) -> Self { Evaluator { env_vars: HashMap::default(), argc: None, @@ -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))) } } } diff --git a/tests/compile-fail/getrandom.rs b/tests/compile-fail/getrandom.rs new file mode 100644 index 0000000000..85148a724e --- /dev/null +++ b/tests/compile-fail/getrandom.rs @@ -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! + } +} diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass/hashmap.rs index d89a5ab535..a663c96595 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass/hashmap.rs @@ -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> = Default::default(); +fn test_map(mut map: HashMap) { map.insert(0, 0); assert_eq!(map.values().fold(0, |x, y| x+y), 0); @@ -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> = Default::default(); + let map_normal: HashMap = HashMap::new(); + + test_map(map); + test_map(map_normal); }