]> git.lizzy.rs Git - rust.git/commitdiff
Implement non-deterministc mode
authorAaron Hill <aa1ronham@gmail.com>
Sun, 7 Apr 2019 22:17:43 +0000 (18:17 -0400)
committerAaron Hill <aa1ronham@gmail.com>
Sun, 7 Apr 2019 23:26:20 +0000 (19:26 -0400)
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.

Cargo.toml
src/bin/miri-rustc-tests.rs
src/bin/miri.rs
src/fn_call.rs
src/lib.rs
tests/compile-fail/getrandom.rs [new file with mode: 0644]
tests/run-pass/hashmap.rs

index d580d4b8ec17e6e44db739ea43ad92423131c6d6..6c77315bea48b238cfe1512b8a405bc672f1f682 100644 (file)
@@ -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"
index e54be4644f25eb13e3d763f47d06c8a90eb8406e..ce2ad1a2715db8527cf8201635a4526ce8a9cd69 100644 (file)
@@ -48,7 +48,7 @@ impl<'a, 'tcx: 'a, 'hir> itemlikevisit::ItemLikeVisitor<'hir> for Visitor<'a, 't
                     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 @@ fn visit_impl_item(&mut self, _impl_item: &'hir hir::ImplItem) {}
                 }
                 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();
index 4cc0d955d3c217ce17bb9b185967b5cf32f083a5..7f15d00e2c8abd5ab71b2080c5432d6e7665dc41 100644 (file)
@@ -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;
@@ -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);
+                    }
                 }
             }
         }
@@ -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);
index 038f5ed8a047588dca0f12c262cd820a356bc364..ba610e8b230c25fa36bcb38d82767b30622710d0 100644 (file)
@@ -4,6 +4,8 @@
 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 @@ fn emulate_foreign_item(
                 // 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(
index be58c6a6a464262661532a90073af66ad5cce895..9b4b69b6d707270883bf52f69f70a7857b709c82 100644 (file)
 
 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<String>,
+
+    // The seed to use when non-determinism is required (e.g. getrandom())
+    pub seed: Option<u64>
 }
 
 // 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<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,
@@ -339,6 +350,7 @@ fn new(validate: bool) -> Self {
             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 (file)
index 0000000..85148a7
--- /dev/null
@@ -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!
+    }
+}
index d89a5ab535f64699d84563b653dd79a127752813..a663c9659524cea676193fcf881bfb1d7ef9cf97 100644 (file)
@@ -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);
 
@@ -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);
 }