From 73239573c9290346dd31c50cd3565cb754d86c80 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Apr 2019 18:17:43 -0400 Subject: [PATCH 01/20] Implement non-deterministc mode Part of #653 This allows us to properly implement getrandom(), which unlocks the default HashMap type (e.g. HashMap) with RandomState) This commit adds a new '-Zmiri-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 | 2 ++ src/bin/miri-rustc-tests.rs | 4 ++-- src/bin/miri.rs | 20 ++++++++++++++++++-- src/fn_call.rs | 31 ++++++++++++++++++++++++++++--- src/lib.rs | 16 ++++++++++++++-- tests/compile-fail/getrandom.rs | 10 ++++++++++ tests/run-pass/hashmap.rs | 16 +++++++++++++--- 7 files changed, 87 insertions(+), 12 deletions(-) create mode 100644 tests/compile-fail/getrandom.rs 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); } From 312f938e791178eccca51ee22126d1ef06f50f9b Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Apr 2019 19:30:37 -0400 Subject: [PATCH 02/20] Fix benchmark --- benches/helpers/miri_helper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs index 01fef16e4a..44a94cd61a 100644 --- a/benches/helpers/miri_helper.rs +++ b/benches/helpers/miri_helper.rs @@ -24,7 +24,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls<'_> { ); self.bencher.iter(|| { - let config = miri::MiriConfig { validate: true, args: vec![] }; + let config = miri::MiriConfig { validate: true, args: vec![], seed: None }; eval_main(tcx, entry_def_id, config); }); }); From dddeda7f7d14ef410bf938e3197587cc8d5ac7da Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Apr 2019 19:57:14 -0400 Subject: [PATCH 03/20] Use getrandom() syscall number from libc --- Cargo.toml | 1 + src/fn_call.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6c77315bea..b0e278c1b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ shell-escape = "0.1.4" rustc-workspace-hack = "1.0.0" hex = "0.3.2" rand = "0.6.5" +libc = "0.2.51" [build-dependencies] vergen = "3" diff --git a/src/fn_call.rs b/src/fn_call.rs index ba610e8b23..74352302f3 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -216,8 +216,8 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' // // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way. - match this.read_scalar(args[0])?.to_usize(this)? { - 318 | 511 => { + match this.read_scalar(args[0])?.to_usize(this)? as i64 { + libc::SYS_getrandom => { match this.machine.rng.as_ref() { Some(rng) => { let ptr = this.read_scalar(args[1])?.to_ptr()?; From 808b1496710b369ceeddc0ef833af12c663ef6e4 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 7 Apr 2019 20:08:15 -0400 Subject: [PATCH 04/20] Use raw syscall numbers --- Cargo.toml | 1 - src/fn_call.rs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b0e278c1b5..6c77315bea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,6 @@ shell-escape = "0.1.4" rustc-workspace-hack = "1.0.0" hex = "0.3.2" rand = "0.6.5" -libc = "0.2.51" [build-dependencies] vergen = "3" diff --git a/src/fn_call.rs b/src/fn_call.rs index 74352302f3..52f252da5c 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -217,7 +217,8 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way. match this.read_scalar(args[0])?.to_usize(this)? as i64 { - libc::SYS_getrandom => { + // SYS_getrandom on x86_64 and x86 respectively + 318 | 355 => { match this.machine.rng.as_ref() { Some(rng) => { let ptr = this.read_scalar(args[1])?.to_ptr()?; From 5530d295ad2c4900c3c8ab80abb5b5258b99446b Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 21:36:35 -0400 Subject: [PATCH 05/20] Simplify cast using as_mut_ptr() --- tests/compile-fail/getrandom.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile-fail/getrandom.rs b/tests/compile-fail/getrandom.rs index 85148a724e..58a39ea315 100644 --- a/tests/compile-fail/getrandom.rs +++ b/tests/compile-fail/getrandom.rs @@ -4,7 +4,7 @@ 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); + libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr() as *mut libc::c_void, 5, 0); //~^ ERROR constant evaluation error: miri does not support random number generators in deterministic mode! } } From 6b0440e26d3e51aef9841ac47914b729416b25c9 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 21:40:52 -0400 Subject: [PATCH 06/20] Cleanup argument parsing --- src/bin/miri.rs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 7f15d00e2c..6e68f803f8 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -146,23 +146,22 @@ fn main() { "--" => { after_dashdash = true; } - _ => { - 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); + arg if arg.starts_with("-Zmiri-seed=") => { + if seed.is_some() { + panic!("Cannot specify -Zmiri-seed multiple times!"); + } + let seed_raw = hex::decode(arg.trim_start_matches("-Zmiri-seed=")).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(&seed_raw); + seed = Some(u64::from_be_bytes(bytes)); + + }, + _ => { + rustc_args.push(arg); } } } From b120e8bb88271bc7fbe68651f230ead35de098a1 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 21:42:12 -0400 Subject: [PATCH 07/20] Only run test with default hasher --- tests/run-pass/hashmap.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass/hashmap.rs index a663c96595..94add0078f 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass/hashmap.rs @@ -27,9 +27,8 @@ fn test_map(mut map: HashMap) { } fn main() { - let map : HashMap> = Default::default(); + let _map : HashMap> = Default::default(); let map_normal: HashMap = HashMap::new(); - test_map(map); test_map(map_normal); } From 6d3e93c2815c61e3293c85189dce1424c842bfc5 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 22:22:41 -0400 Subject: [PATCH 08/20] Refactor random number generation --- src/fn_call.rs | 61 ++++++++++++++++++++------------- tests/compile-fail/getrandom.rs | 2 +- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 52f252da5c..0f1814a0c4 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -215,36 +215,22 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' // figure out some way to actually process some of them. // // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` - // is called if a `HashMap` is created the regular way. + // is called if a `HashMap` is created the regular way (e.g. HashMap). match this.read_scalar(args[0])?.to_usize(this)? as i64 { // SYS_getrandom on x86_64 and x86 respectively 318 | 355 => { - 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)?; + 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()?; + // 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)? + let data = gen_random(this, len as usize)?; + 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(), - )) - } - } - + this.write_scalar(Scalar::from_uint(len, dest.layout.size), dest)?; } id => { return err!(Unimplemented( @@ -768,6 +754,17 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' "GetCommandLineW" => { this.write_scalar(Scalar::Ptr(this.machine.cmd_line.unwrap()), dest)?; } + // The actual name of 'RtlGenRandom' + "SystemFunction036" => { + let ptr = this.read_scalar(args[1])?.to_ptr()?; + let len = this.read_scalar(args[2])?.to_usize(this)?; + + let data = gen_random(this, len as usize)?; + this.memory_mut().get_mut(ptr.alloc_id)? + .write_bytes(tcx, ptr, &data)?; + + this.write_scalar(Scalar::from_bool(true), dest)?; + } // We can't execute anything else. _ => { @@ -786,3 +783,21 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest) } } + +fn gen_random<'a, 'mir, 'tcx>(this: &mut MiriEvalContext<'a, 'mir, 'tcx>, + len: usize) -> Result, EvalError<'tcx>> { + + match this.machine.rng.as_ref() { + Some(rng) => { + let mut data = vec![0; len]; + rng.borrow_mut().fill_bytes(&mut data); + Ok(data) + } + None => { + err!(Unimplemented( + "miri does not support random number generators in deterministic mode! + Use '-Zmiri-seed=' to enable random number generation".to_owned(), + )) + } + } +} diff --git a/tests/compile-fail/getrandom.rs b/tests/compile-fail/getrandom.rs index 58a39ea315..efa7a86fee 100644 --- a/tests/compile-fail/getrandom.rs +++ b/tests/compile-fail/getrandom.rs @@ -4,7 +4,7 @@ extern crate libc; fn main() { let mut buf = [0u8; 5]; unsafe { - libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr() as *mut libc::c_void, 5, 0); + libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr() as *mut libc::c_void, 5 as libc::size_t, 0 as libc::c_uint); //~^ ERROR constant evaluation error: miri does not support random number generators in deterministic mode! } } From 858e82bc6f56114545f2be472210ebef84d2e4b2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 22:29:40 -0400 Subject: [PATCH 09/20] Disable normal HashMap test on OS X Implementing random number generation on OS X will require special-casing the 'openat' system call to special-case reading from /dev/urandom --- tests/run-pass/hashmap.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass/hashmap.rs index 94add0078f..d0de8b448b 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass/hashmap.rs @@ -28,7 +28,12 @@ fn test_map(mut map: HashMap) { fn main() { let _map : HashMap> = Default::default(); - let map_normal: HashMap = HashMap::new(); - test_map(map_normal); + // TODO: Implement random number generation on OS X + if cfg!(not(target_os = "darwin")) { + let map_normal: HashMap = HashMap::new(); + test_map(map_normal); + } else { + test_map(_map); + } } From 5f997645bccb5776d17deafb8101c7575b41c08f Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 22:37:58 -0400 Subject: [PATCH 10/20] Interpret system call numbers relative to target architecture --- src/fn_call.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 0f1814a0c4..94e1f11ccf 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -216,9 +216,9 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' // // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). - match this.read_scalar(args[0])?.to_usize(this)? as i64 { + match (this.read_scalar(args[0])?.to_usize(this)? as i64, tcx.data_layout.pointer_size.bits()) { // SYS_getrandom on x86_64 and x86 respectively - 318 | 355 => { + (318, 64) | (355, 32) => { let ptr = this.read_scalar(args[1])?.to_ptr()?; let len = this.read_scalar(args[2])?.to_usize(this)?; @@ -232,7 +232,7 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' this.write_scalar(Scalar::from_uint(len, dest.layout.size), dest)?; } - id => { + (id, _size) => { return err!(Unimplemented( format!("miri does not support syscall ID {}", id), )) From 174874420b09082d4feef0fe25a6cdf14fca3309 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 22:48:57 -0400 Subject: [PATCH 11/20] OS X is "macos", not "darwin" --- tests/run-pass/hashmap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass/hashmap.rs index d0de8b448b..a95f7170aa 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass/hashmap.rs @@ -30,7 +30,7 @@ fn main() { let _map : HashMap> = Default::default(); // TODO: Implement random number generation on OS X - if cfg!(not(target_os = "darwin")) { + if cfg!(not(target_os = "macos")) { let map_normal: HashMap = HashMap::new(); test_map(map_normal); } else { From 5e07ff6b1f1f5e8906a571a59855013ee49bdd30 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 23:16:13 -0400 Subject: [PATCH 12/20] Only run 'getrandom' test on Linux --- tests/compile-fail/getrandom.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/compile-fail/getrandom.rs b/tests/compile-fail/getrandom.rs index efa7a86fee..dcd3869c14 100644 --- a/tests/compile-fail/getrandom.rs +++ b/tests/compile-fail/getrandom.rs @@ -1,3 +1,5 @@ +// only-linux + #![feature(rustc_private)] extern crate libc; From 6b4c5b81da314ff29adcf341ba80faff00fa1bb0 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 23:18:10 -0400 Subject: [PATCH 13/20] Fix 'RtlGenRandom' argument slots --- src/fn_call.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 94e1f11ccf..8a586c7705 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -756,8 +756,8 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' } // The actual name of 'RtlGenRandom' "SystemFunction036" => { - let ptr = this.read_scalar(args[1])?.to_ptr()?; - let len = this.read_scalar(args[2])?.to_usize(this)?; + let ptr = this.read_scalar(args[0])?.to_ptr()?; + let len = this.read_scalar(args[1])?.to_usize(this)?; let data = gen_random(this, len as usize)?; this.memory_mut().get_mut(ptr.alloc_id)? From 92436805885c28cfee7cb52d2b2fe742e4c3cb4f Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Mon, 8 Apr 2019 23:52:33 -0400 Subject: [PATCH 14/20] Use 'ignore-' instead of 'only-' Apparently 'ignore-' doesn't work with compiletest_rs --- tests/compile-fail/getrandom.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/compile-fail/getrandom.rs b/tests/compile-fail/getrandom.rs index dcd3869c14..7f2087a320 100644 --- a/tests/compile-fail/getrandom.rs +++ b/tests/compile-fail/getrandom.rs @@ -1,4 +1,5 @@ -// only-linux +// ignore-macos +// ignore-windows #![feature(rustc_private)] extern crate libc; From 22044c878d2a29902d05a4d66604f797207570d7 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 9 Apr 2019 10:16:32 -0400 Subject: [PATCH 15/20] Improve deterministic mode error message --- src/fn_call.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 8a586c7705..b97c237412 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -795,8 +795,10 @@ fn gen_random<'a, 'mir, 'tcx>(this: &mut MiriEvalContext<'a, 'mir, 'tcx>, } None => { err!(Unimplemented( - "miri does not support random number generators in deterministic mode! - Use '-Zmiri-seed=' to enable random number generation".to_owned(), + "miri does not support gathering system entropy in deterministic mode! + Use '-Zmiri-seed=' to enable random number generation. + WARNING: Miri does *not* generate cryptographically secure entropy - + do not use Miri to run any program that need secure random number generation".to_owned(), )) } } From ae8e7f654a002d182fe66cdbd3ebfe0ee1e74ae2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 9 Apr 2019 10:19:29 -0400 Subject: [PATCH 16/20] Fix compile-fail error message --- tests/compile-fail/getrandom.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile-fail/getrandom.rs b/tests/compile-fail/getrandom.rs index 7f2087a320..4dc3e863aa 100644 --- a/tests/compile-fail/getrandom.rs +++ b/tests/compile-fail/getrandom.rs @@ -8,6 +8,6 @@ fn main() { let mut buf = [0u8; 5]; unsafe { libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr() as *mut libc::c_void, 5 as libc::size_t, 0 as libc::c_uint); - //~^ ERROR constant evaluation error: miri does not support random number generators in deterministic mode! + //~^ ERROR constant evaluation error: miri does not support gathering system entropy in deterministic mode! } } From c6e0d097143a90ec8512a087b6cd3a475d2aeff1 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 9 Apr 2019 11:04:30 -0400 Subject: [PATCH 17/20] Retrieve SYS_getrandom from libc using const-eval --- src/fn_call.rs | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index b97c237412..3eacaaa74e 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -211,14 +211,14 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' } "syscall" => { - // TODO: read `syscall` IDs like `sysconf` IDs and - // figure out some way to actually process some of them. - // + let sys_getrandom = this.eval_path_scalar(&["libc", "SYS_getrandom"])? + .expect("Failed to get libc::SYS_getrandom") + .to_usize(this)? as i64; + // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). - match (this.read_scalar(args[0])?.to_usize(this)? as i64, tcx.data_layout.pointer_size.bits()) { - // SYS_getrandom on x86_64 and x86 respectively - (318, 64) | (355, 32) => { + match this.read_scalar(args[0])?.to_usize(this)? as i64 { + id if id == sys_getrandom => { let ptr = this.read_scalar(args[1])?.to_ptr()?; let len = this.read_scalar(args[2])?.to_usize(this)?; @@ -232,7 +232,7 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' this.write_scalar(Scalar::from_uint(len, dest.layout.size), dest)?; } - (id, _size) => { + id => { return err!(Unimplemented( format!("miri does not support syscall ID {}", id), )) @@ -496,18 +496,13 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' ]; let mut result = None; for &(path, path_value) in paths { - if let Ok(instance) = this.resolve_path(path) { - let cid = GlobalId { - instance, - promoted: None, - }; - let const_val = this.const_eval_raw(cid)?; - let const_val = this.read_scalar(const_val.into())?; - let value = const_val.to_i32()?; - if value == name { + if let Some(val) = this.eval_path_scalar(path)? { + let val = val.to_i32()?; + if val == name { result = Some(path_value); break; } + } } if let Some(result) = result { @@ -782,6 +777,22 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx> { self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest) } + + /// Evaluates the scalar at the specified path. Returns Some(val) + /// if the path could be resolved, and None otherwise + fn eval_path_scalar(&mut self, path: &[&str]) -> EvalResult<'tcx, Option>> { + let this = self.eval_context_mut(); + if let Ok(instance) = this.resolve_path(path) { + let cid = GlobalId { + instance, + promoted: None, + }; + let const_val = this.const_eval_raw(cid)?; + let const_val = this.read_scalar(const_val.into())?; + return Ok(Some(const_val)); + } + return Ok(None); + } } fn gen_random<'a, 'mir, 'tcx>(this: &mut MiriEvalContext<'a, 'mir, 'tcx>, From 0837d630f7becb8259074a2adc35e77476e5083e Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 9 Apr 2019 15:49:34 -0400 Subject: [PATCH 18/20] Some final cleanup --- src/fn_call.rs | 14 ++++++++------ src/lib.rs | 5 ++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index 3eacaaa74e..50351e2c70 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -213,11 +213,11 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' "syscall" => { let sys_getrandom = this.eval_path_scalar(&["libc", "SYS_getrandom"])? .expect("Failed to get libc::SYS_getrandom") - .to_usize(this)? as i64; + .to_usize(this)?; // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). - match this.read_scalar(args[0])?.to_usize(this)? as i64 { + match this.read_scalar(args[0])?.to_usize(this)? { id if id == sys_getrandom => { let ptr = this.read_scalar(args[1])?.to_ptr()?; let len = this.read_scalar(args[2])?.to_usize(this)?; @@ -795,13 +795,15 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<' } } -fn gen_random<'a, 'mir, 'tcx>(this: &mut MiriEvalContext<'a, 'mir, 'tcx>, - len: usize) -> Result, EvalError<'tcx>> { +fn gen_random<'a, 'mir, 'tcx>( + this: &mut MiriEvalContext<'a, 'mir, 'tcx>, + len: usize, +) -> Result, EvalError<'tcx>> { - match this.machine.rng.as_ref() { + match &mut this.machine.rng { Some(rng) => { let mut data = vec![0; len]; - rng.borrow_mut().fill_bytes(&mut data); + rng.fill_bytes(&mut data); Ok(data) } None => { diff --git a/src/lib.rs b/src/lib.rs index 9b4b69b6d7..d185ec610c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,6 @@ mod stacked_borrows; use std::collections::HashMap; use std::borrow::Cow; -use std::cell::RefCell; use rand::rngs::StdRng; use rand::SeedableRng; @@ -336,7 +335,7 @@ pub struct Evaluator<'tcx> { /// The random number generator to use if Miri /// is running in non-deterministic mode - pub(crate) rng: Option> + pub(crate) rng: Option } impl<'tcx> Evaluator<'tcx> { @@ -350,7 +349,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))) + rng: seed.map(|s| StdRng::seed_from_u64(s)) } } } From 48b22b80c5e4977fd638d67e9494d85df5cb912e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 9 Apr 2019 23:33:13 -0400 Subject: [PATCH 19/20] Fix typo Co-Authored-By: Aaron1011 --- src/fn_call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index a0ff614de2..3e12c1dd1c 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -826,7 +826,7 @@ fn gen_random<'a, 'mir, 'tcx>( "miri does not support gathering system entropy in deterministic mode! Use '-Zmiri-seed=' to enable random number generation. WARNING: Miri does *not* generate cryptographically secure entropy - - do not use Miri to run any program that need secure random number generation".to_owned(), + do not use Miri to run any program that needs secure random number generation".to_owned(), )) } } From a8763f3d8d8e362f49090e8d99a86920f2e55fef Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 9 Apr 2019 23:36:27 -0400 Subject: [PATCH 20/20] Don't create HashMap when not needed --- tests/run-pass/hashmap.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass/hashmap.rs index a95f7170aa..b29b681939 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass/hashmap.rs @@ -27,13 +27,12 @@ fn test_map(mut map: HashMap) { } fn main() { - let _map : HashMap> = Default::default(); - // TODO: Implement random number generation on OS X if cfg!(not(target_os = "macos")) { let map_normal: HashMap = HashMap::new(); test_map(map_normal); } else { - test_map(_map); + let map : HashMap> = Default::default(); + test_map(map); } }