Skip to content

Commit 2dc6e8b

Browse files
authored
Merge pull request #683 from Aaron1011/feature/random
Implement non-deterministc mode
2 parents 28f2e5b + a8763f3 commit 2dc6e8b

File tree

8 files changed

+136
-26
lines changed

8 files changed

+136
-26
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ shell-escape = "0.1.4"
4444
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
4545
# for more information.
4646
rustc-workspace-hack = "1.0.0"
47+
hex = "0.3.2"
48+
rand = "0.6.5"
4749

4850
[build-dependencies]
4951
vergen = "3"

benches/helpers/miri_helper.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls<'_> {
2424
);
2525

2626
self.bencher.iter(|| {
27-
let config = miri::MiriConfig { validate: true, args: vec![] };
27+
let config = miri::MiriConfig { validate: true, args: vec![], seed: None };
2828
eval_main(tcx, entry_def_id, config);
2929
});
3030
});

src/bin/miri-rustc-tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
4848
fn visit_item(&mut self, i: &'hir hir::Item) {
4949
if let hir::ItemKind::Fn(.., body_id) = i.node {
5050
if i.attrs.iter().any(|attr| attr.check_name("test")) {
51-
let config = MiriConfig { validate: true, args: vec![] };
51+
let config = MiriConfig { validate: true, args: vec![], seed: None };
5252
let did = self.0.hir().body_owner_def_id(body_id);
5353
println!("running test: {}", self.0.def_path_debug_str(did));
5454
miri::eval_main(self.0, did, config);
@@ -61,7 +61,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
6161
}
6262
tcx.hir().krate().visit_all_item_likes(&mut Visitor(tcx));
6363
} else if let Some((entry_def_id, _)) = tcx.entry_fn(LOCAL_CRATE) {
64-
let config = MiriConfig { validate: true, args: vec![] };
64+
let config = MiriConfig { validate: true, args: vec![], seed: None };
6565
miri::eval_main(tcx, entry_def_id, config);
6666

6767
compiler.session().abort_if_errors();

src/bin/miri.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fn main() {
126126

127127
// Parse our arguments and split them across `rustc` and `miri`.
128128
let mut validate = true;
129+
let mut seed: Option<u64> = None;
129130
let mut rustc_args = vec![];
130131
let mut miri_args = vec![];
131132
let mut after_dashdash = false;
@@ -145,6 +146,20 @@ fn main() {
145146
"--" => {
146147
after_dashdash = true;
147148
}
149+
arg if arg.starts_with("-Zmiri-seed=") => {
150+
if seed.is_some() {
151+
panic!("Cannot specify -Zmiri-seed multiple times!");
152+
}
153+
let seed_raw = hex::decode(arg.trim_start_matches("-Zmiri-seed=")).unwrap();
154+
if seed_raw.len() > 8 {
155+
panic!(format!("-Zmiri-seed must be at most 8 bytes, was {}", seed_raw.len()));
156+
}
157+
158+
let mut bytes = [0; 8];
159+
bytes[..seed_raw.len()].copy_from_slice(&seed_raw);
160+
seed = Some(u64::from_be_bytes(bytes));
161+
162+
},
148163
_ => {
149164
rustc_args.push(arg);
150165
}
@@ -163,7 +178,7 @@ fn main() {
163178

164179
debug!("rustc arguments: {:?}", rustc_args);
165180
debug!("miri arguments: {:?}", miri_args);
166-
let miri_config = miri::MiriConfig { validate, args: miri_args };
181+
let miri_config = miri::MiriConfig { validate, args: miri_args, seed };
167182
let result = rustc_driver::report_ices_to_stderr_if_any(move || {
168183
rustc_driver::run_compiler(&rustc_args, &mut MiriCompilerCalls { miri_config }, None, None)
169184
}).and_then(|result| result);

src/fn_call.rs

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use rustc::hir::def_id::DefId;
44
use rustc::mir;
55
use syntax::attr;
66

7+
use rand::RngCore;
8+
79
use crate::*;
810

911
impl<'a, 'mir, 'tcx> EvalContextExt<'a, 'mir, 'tcx> for crate::MiriEvalContext<'a, 'mir, 'tcx> {}
@@ -224,16 +226,26 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
224226
}
225227

226228
"syscall" => {
227-
// TODO: read `syscall` IDs like `sysconf` IDs and
228-
// figure out some way to actually process some of them.
229-
//
229+
let sys_getrandom = this.eval_path_scalar(&["libc", "SYS_getrandom"])?
230+
.expect("Failed to get libc::SYS_getrandom")
231+
.to_usize(this)?;
232+
230233
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
231-
// is called if a `HashMap` is created the regular way.
234+
// is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
232235
match this.read_scalar(args[0])?.to_usize(this)? {
233-
318 | 511 => {
234-
return err!(Unimplemented(
235-
"miri does not support random number generators".to_owned(),
236-
))
236+
id if id == sys_getrandom => {
237+
let ptr = this.read_scalar(args[1])?.to_ptr()?;
238+
let len = this.read_scalar(args[2])?.to_usize(this)?;
239+
240+
// The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
241+
// neither of which have any effect on our current PRNG
242+
let _flags = this.read_scalar(args[3])?.to_i32()?;
243+
244+
let data = gen_random(this, len as usize)?;
245+
this.memory_mut().get_mut(ptr.alloc_id)?
246+
.write_bytes(tcx, ptr, &data)?;
247+
248+
this.write_scalar(Scalar::from_uint(len, dest.layout.size), dest)?;
237249
}
238250
id => {
239251
return err!(Unimplemented(
@@ -499,18 +511,13 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
499511
];
500512
let mut result = None;
501513
for &(path, path_value) in paths {
502-
if let Ok(instance) = this.resolve_path(path) {
503-
let cid = GlobalId {
504-
instance,
505-
promoted: None,
506-
};
507-
let const_val = this.const_eval_raw(cid)?;
508-
let const_val = this.read_scalar(const_val.into())?;
509-
let value = const_val.to_i32()?;
510-
if value == name {
514+
if let Some(val) = this.eval_path_scalar(path)? {
515+
let val = val.to_i32()?;
516+
if val == name {
511517
result = Some(path_value);
512518
break;
513519
}
520+
514521
}
515522
}
516523
if let Some(result) = result {
@@ -757,6 +764,17 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
757764
"GetCommandLineW" => {
758765
this.write_scalar(Scalar::Ptr(this.machine.cmd_line.unwrap()), dest)?;
759766
}
767+
// The actual name of 'RtlGenRandom'
768+
"SystemFunction036" => {
769+
let ptr = this.read_scalar(args[0])?.to_ptr()?;
770+
let len = this.read_scalar(args[1])?.to_usize(this)?;
771+
772+
let data = gen_random(this, len as usize)?;
773+
this.memory_mut().get_mut(ptr.alloc_id)?
774+
.write_bytes(tcx, ptr, &data)?;
775+
776+
this.write_scalar(Scalar::from_bool(true), dest)?;
777+
}
760778

761779
// We can't execute anything else.
762780
_ => {
@@ -774,4 +792,42 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
774792
fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx> {
775793
self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest)
776794
}
795+
796+
/// Evaluates the scalar at the specified path. Returns Some(val)
797+
/// if the path could be resolved, and None otherwise
798+
fn eval_path_scalar(&mut self, path: &[&str]) -> EvalResult<'tcx, Option<ScalarMaybeUndef<stacked_borrows::Borrow>>> {
799+
let this = self.eval_context_mut();
800+
if let Ok(instance) = this.resolve_path(path) {
801+
let cid = GlobalId {
802+
instance,
803+
promoted: None,
804+
};
805+
let const_val = this.const_eval_raw(cid)?;
806+
let const_val = this.read_scalar(const_val.into())?;
807+
return Ok(Some(const_val));
808+
}
809+
return Ok(None);
810+
}
811+
}
812+
813+
fn gen_random<'a, 'mir, 'tcx>(
814+
this: &mut MiriEvalContext<'a, 'mir, 'tcx>,
815+
len: usize,
816+
) -> Result<Vec<u8>, EvalError<'tcx>> {
817+
818+
match &mut this.machine.rng {
819+
Some(rng) => {
820+
let mut data = vec![0; len];
821+
rng.fill_bytes(&mut data);
822+
Ok(data)
823+
}
824+
None => {
825+
err!(Unimplemented(
826+
"miri does not support gathering system entropy in deterministic mode!
827+
Use '-Zmiri-seed=<seed>' to enable random number generation.
828+
WARNING: Miri does *not* generate cryptographically secure entropy -
829+
do not use Miri to run any program that needs secure random number generation".to_owned(),
830+
))
831+
}
832+
}
777833
}

src/lib.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ mod stacked_borrows;
2424
use std::collections::HashMap;
2525
use std::borrow::Cow;
2626

27+
use rand::rngs::StdRng;
28+
use rand::SeedableRng;
29+
2730
use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
2831
use rustc::ty::layout::{LayoutOf, Size, Align};
2932
use rustc::hir::{self, def_id::DefId};
@@ -60,6 +63,9 @@ pub fn miri_default_args() -> &'static [&'static str] {
6063
pub struct MiriConfig {
6164
pub validate: bool,
6265
pub args: Vec<String>,
66+
67+
// The seed to use when non-determinism is required (e.g. getrandom())
68+
pub seed: Option<u64>
6369
}
6470

6571
// Used by priroda.
@@ -71,7 +77,7 @@ pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
7177
let mut ecx = InterpretCx::new(
7278
tcx.at(syntax::source_map::DUMMY_SP),
7379
ty::ParamEnv::reveal_all(),
74-
Evaluator::new(config.validate),
80+
Evaluator::new(config.validate, config.seed),
7581
);
7682

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

327333
/// Stacked Borrows state.
328334
pub(crate) stacked_borrows: stacked_borrows::State,
335+
336+
/// The random number generator to use if Miri
337+
/// is running in non-deterministic mode
338+
pub(crate) rng: Option<StdRng>
329339
}
330340

331341
impl<'tcx> Evaluator<'tcx> {
332-
fn new(validate: bool) -> Self {
342+
fn new(validate: bool, seed: Option<u64>) -> Self {
333343
Evaluator {
334344
env_vars: HashMap::default(),
335345
argc: None,
@@ -339,6 +349,7 @@ impl<'tcx> Evaluator<'tcx> {
339349
tls: TlsData::default(),
340350
validate,
341351
stacked_borrows: stacked_borrows::State::default(),
352+
rng: seed.map(|s| StdRng::seed_from_u64(s))
342353
}
343354
}
344355
}

tests/compile-fail/getrandom.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// ignore-macos
2+
// ignore-windows
3+
4+
#![feature(rustc_private)]
5+
extern crate libc;
6+
7+
fn main() {
8+
let mut buf = [0u8; 5];
9+
unsafe {
10+
libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr() as *mut libc::c_void, 5 as libc::size_t, 0 as libc::c_uint);
11+
//~^ ERROR constant evaluation error: miri does not support gathering system entropy in deterministic mode!
12+
}
13+
}

tests/run-pass/hashmap.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
// compile-flags: -Zmiri-seed=0000000000000000
2+
13
use std::collections::{self, HashMap};
2-
use std::hash::BuildHasherDefault;
4+
use std::hash::{BuildHasherDefault, BuildHasher};
35

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

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

2425
// TODO: Test Entry API, Iterators, ...
26+
27+
}
28+
29+
fn main() {
30+
// TODO: Implement random number generation on OS X
31+
if cfg!(not(target_os = "macos")) {
32+
let map_normal: HashMap<i32, i32> = HashMap::new();
33+
test_map(map_normal);
34+
} else {
35+
let map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = Default::default();
36+
test_map(map);
37+
}
2538
}

0 commit comments

Comments
 (0)