Skip to content

Commit 8de4f34

Browse files
committed
Merge remote-tracking branch 'origin/master'
2 parents 83d199e + 2dc6e8b commit 8de4f34

File tree

9 files changed

+178
-27
lines changed

9 files changed

+178
-27
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
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"
@@ -54,5 +56,5 @@ cargo_miri = ["cargo_metadata", "directories", "rustc_version"]
5456
rustc_tests = []
5557

5658
[dev-dependencies]
57-
compiletest_rs = { version = "=0.3.19", features = ["tmp", "stable"] }
59+
compiletest_rs = { version = "0.3.21", features = ["tmp", "stable"] }
5860
colored = "1.6"

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: 88 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> {}
@@ -93,6 +95,21 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
9395
this.write_scalar(Scalar::Ptr(ptr.with_default_tag()), dest)?;
9496
}
9597
}
98+
"calloc" => {
99+
let items = this.read_scalar(args[0])?.to_usize(this)?;
100+
let len = this.read_scalar(args[1])?.to_usize(this)?;
101+
let bytes = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
102+
103+
if bytes == 0 {
104+
this.write_null(dest)?;
105+
} else {
106+
let size = Size::from_bytes(bytes);
107+
let align = this.tcx.data_layout.pointer_align.abi;
108+
let ptr = this.memory_mut().allocate(size, align, MiriMemoryKind::C.into()).with_default_tag();
109+
this.memory_mut().get_mut(ptr.alloc_id)?.write_repeat(tcx, ptr, 0, size)?;
110+
this.write_scalar(Scalar::Ptr(ptr), dest)?;
111+
}
112+
}
96113
"posix_memalign" => {
97114
let ret = this.deref_operand(args[0])?;
98115
let align = this.read_scalar(args[1])?.to_usize(this)?;
@@ -209,16 +226,26 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
209226
}
210227

211228
"syscall" => {
212-
// TODO: read `syscall` IDs like `sysconf` IDs and
213-
// figure out some way to actually process some of them.
214-
//
229+
let sys_getrandom = this.eval_path_scalar(&["libc", "SYS_getrandom"])?
230+
.expect("Failed to get libc::SYS_getrandom")
231+
.to_usize(this)?;
232+
215233
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
216-
// 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>).
217235
match this.read_scalar(args[0])?.to_usize(this)? {
218-
318 | 511 => {
219-
return err!(Unimplemented(
220-
"miri does not support random number generators".to_owned(),
221-
))
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)?;
222249
}
223250
id => {
224251
return err!(Unimplemented(
@@ -484,18 +511,13 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
484511
];
485512
let mut result = None;
486513
for &(path, path_value) in paths {
487-
if let Ok(instance) = this.resolve_path(path) {
488-
let cid = GlobalId {
489-
instance,
490-
promoted: None,
491-
};
492-
let const_val = this.const_eval_raw(cid)?;
493-
let const_val = this.read_scalar(const_val.into())?;
494-
let value = const_val.to_i32()?;
495-
if value == name {
514+
if let Some(val) = this.eval_path_scalar(path)? {
515+
let val = val.to_i32()?;
516+
if val == name {
496517
result = Some(path_value);
497518
break;
498519
}
520+
499521
}
500522
}
501523
if let Some(result) = result {
@@ -742,6 +764,17 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
742764
"GetCommandLineW" => {
743765
this.write_scalar(Scalar::Ptr(this.machine.cmd_line.unwrap()), dest)?;
744766
}
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+
}
745778

746779
// We can't execute anything else.
747780
_ => {
@@ -759,4 +792,42 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
759792
fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx> {
760793
self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest)
761794
}
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+
}
762833
}

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);
@@ -324,10 +330,14 @@ pub struct Evaluator<'tcx> {
324330

325331
/// Stacked Borrows state.
326332
pub(crate) stacked_borrows: stacked_borrows::State,
333+
334+
/// The random number generator to use if Miri
335+
/// is running in non-deterministic mode
336+
pub(crate) rng: Option<StdRng>
327337
}
328338

329339
impl<'tcx> Evaluator<'tcx> {
330-
fn new(validate: bool) -> Self {
340+
fn new(validate: bool, seed: Option<u64>) -> Self {
331341
Evaluator {
332342
env_vars: HashMap::default(),
333343
argc: None,
@@ -337,6 +347,7 @@ impl<'tcx> Evaluator<'tcx> {
337347
tls: TlsData::default(),
338348
validate,
339349
stacked_borrows: stacked_borrows::State::default(),
350+
rng: seed.map(|s| StdRng::seed_from_u64(s))
340351
}
341352
}
342353
}

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/calloc.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//ignore-windows: Uses POSIX APIs
2+
3+
#![feature(rustc_private)]
4+
5+
use core::slice;
6+
7+
extern crate libc;
8+
9+
fn main() {
10+
unsafe {
11+
let p1 = libc::calloc(0, 0);
12+
assert!(p1.is_null());
13+
14+
let p2 = libc::calloc(20, 0);
15+
assert!(p2.is_null());
16+
17+
let p3 = libc::calloc(0, 20);
18+
assert!(p3.is_null());
19+
20+
let p4 = libc::calloc(4, 8);
21+
assert!(!p4.is_null());
22+
let slice = slice::from_raw_parts(p4 as *const u8, 4 * 8);
23+
assert_eq!(&slice, &[0_u8; 4 * 8]);
24+
libc::free(p4);
25+
}
26+
}

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)