Skip to content

Commit 1f504ea

Browse files
committed
Auto merge of #894 - christianpoveda:env-vars-communication, r=RalfJung
Enable env communication related issue: #800. r? @RalfJung
2 parents 47b227e + 451a09a commit 1f504ea

File tree

13 files changed

+124
-34
lines changed

13 files changed

+124
-34
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ Several `-Z` flags are relevant for Miri:
157157
is enforced by default. This is mostly useful for debugging; it means Miri
158158
will miss bugs in your program. However, this can also help to make Miri run
159159
faster.
160+
* `-Zmiri-enable-communication` enables communication between the host
161+
environment and Miri, i.e., all the host environment variables are available
162+
during Miri runtime.
160163
* `-Zmir-opt-level` controls how many MIR optimizations are performed. Miri
161164
overrides the default to be `0`; be advised that using any higher level can
162165
make Miri miss bugs in your program because they got optimized away.

benches/helpers/miri_helper.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ impl rustc_driver::Callbacks for MiriCompilerCalls<'_> {
2525
);
2626

2727
self.bencher.iter(|| {
28-
let config = miri::MiriConfig { validate: true, args: vec![], seed: None };
28+
let config = miri::MiriConfig {
29+
validate: true,
30+
communicate: false,
31+
args: vec![],
32+
seed: None,
33+
};
2934
eval_main(tcx, entry_def_id, config);
3035
});
3136
});

src/bin/miri-rustc-tests.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ 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(syntax::symbol::sym::test)) {
51-
let config = MiriConfig { validate: true, args: vec![], seed: None };
51+
let config = MiriConfig {
52+
validate: true,
53+
communicate: false,
54+
args: vec![],
55+
seed: None,
56+
};
5257
let did = self.0.hir().body_owner_def_id(body_id);
5358
println!("running test: {}", self.0.def_path_debug_str(did));
5459
miri::eval_main(self.0, did, config);
@@ -61,7 +66,12 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
6166
}
6267
tcx.hir().krate().visit_all_item_likes(&mut Visitor(tcx));
6368
} else if let Some((entry_def_id, _)) = tcx.entry_fn(LOCAL_CRATE) {
64-
let config = MiriConfig { validate: true, args: vec![], seed: None };
69+
let config = MiriConfig {
70+
validate: true,
71+
communicate: false,
72+
args: vec![],
73+
seed: None
74+
};
6575
miri::eval_main(tcx, entry_def_id, config);
6676

6777
compiler.session().abort_if_errors();

src/bin/miri.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ fn main() {
130130

131131
// Parse our arguments and split them across `rustc` and `miri`.
132132
let mut validate = true;
133+
let mut communicate = false;
133134
let mut seed: Option<u64> = None;
134135
let mut rustc_args = vec![];
135136
let mut miri_args = vec![];
@@ -147,6 +148,9 @@ fn main() {
147148
"-Zmiri-disable-validation" => {
148149
validate = false;
149150
},
151+
"-Zmiri-enable-communication" => {
152+
communicate = true;
153+
},
150154
"--" => {
151155
after_dashdash = true;
152156
}
@@ -196,7 +200,7 @@ fn main() {
196200

197201
debug!("rustc arguments: {:?}", rustc_args);
198202
debug!("miri arguments: {:?}", miri_args);
199-
let miri_config = miri::MiriConfig { validate, args: miri_args, seed };
203+
let miri_config = miri::MiriConfig { validate, communicate, args: miri_args, seed };
200204
let result = rustc_driver::report_ices_to_stderr_if_any(move || {
201205
rustc_driver::run_compiler(&rustc_args, &mut MiriCompilerCalls { miri_config }, None, None)
202206
}).and_then(|result| result);

src/eval.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,19 @@ use crate::{
1212
InterpResult, InterpError, InterpCx, StackPopCleanup, struct_error,
1313
Scalar, Tag, Pointer, FnVal,
1414
MemoryExtra, MiriMemoryKind, Evaluator, TlsEvalContextExt, HelpersEvalContextExt,
15+
EnvVars,
1516
};
1617

1718
/// Configuration needed to spawn a Miri instance.
1819
#[derive(Clone)]
1920
pub struct MiriConfig {
21+
/// Determine if validity checking and Stacked Borrows are enabled.
2022
pub validate: bool,
23+
/// Determines if communication with the host environment is enabled.
24+
pub communicate: bool,
2125
pub args: Vec<String>,
22-
23-
// The seed to use when non-determinism is required (e.g. getrandom())
24-
pub seed: Option<u64>
26+
/// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
27+
pub seed: Option<u64>,
2528
}
2629

2730
// Used by priroda.
@@ -33,10 +36,14 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
3336
let mut ecx = InterpCx::new(
3437
tcx.at(syntax::source_map::DUMMY_SP),
3538
ty::ParamEnv::reveal_all(),
36-
Evaluator::new(),
39+
Evaluator::new(config.communicate),
3740
MemoryExtra::new(StdRng::seed_from_u64(config.seed.unwrap_or(0)), config.validate),
3841
);
3942

43+
// Complete initialization.
44+
EnvVars::init(&mut ecx, config.communicate);
45+
46+
// Setup first stack-frame
4047
let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
4148
let main_mir = ecx.load_mir(main_instance.def)?;
4249

@@ -158,7 +165,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
158165
cur_ptr = cur_ptr.offset(char_size, tcx)?;
159166
}
160167
}
161-
168+
162169
assert!(args.next().is_none(), "start lang item has more arguments than expected");
163170

164171
Ok(ecx)

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ pub use crate::shims::foreign_items::EvalContextExt as ForeignItemsEvalContextEx
3333
pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt;
3434
pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData};
3535
pub use crate::shims::dlsym::{Dlsym, EvalContextExt as DlsymEvalContextExt};
36+
pub use crate::shims::env::EnvVars;
3637
pub use crate::operator::EvalContextExt as OperatorEvalContextExt;
3738
pub use crate::range_map::RangeMap;
3839
pub use crate::helpers::{EvalContextExt as HelpersEvalContextExt};

src/machine.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
44
use std::rc::Rc;
55
use std::borrow::Cow;
6-
use std::collections::HashMap;
76
use std::cell::RefCell;
87

98
use rand::rngs::StdRng;
@@ -79,7 +78,7 @@ impl MemoryExtra {
7978
pub struct Evaluator<'tcx> {
8079
/// Environment variables set by `setenv`.
8180
/// Miri does not expose env vars from the host to the emulated program.
82-
pub(crate) env_vars: HashMap<Vec<u8>, Pointer<Tag>>,
81+
pub(crate) env_vars: EnvVars,
8382

8483
/// Program arguments (`Option` because we can only initialize them after creating the ecx).
8584
/// These are *pointers* to argc/argv because macOS.
@@ -93,17 +92,23 @@ pub struct Evaluator<'tcx> {
9392

9493
/// TLS state.
9594
pub(crate) tls: TlsData<'tcx>,
95+
96+
/// If enabled, the `env_vars` field is populated with the host env vars during initialization.
97+
pub(crate) communicate: bool,
9698
}
9799

98100
impl<'tcx> Evaluator<'tcx> {
99-
pub(crate) fn new() -> Self {
101+
pub(crate) fn new(communicate: bool) -> Self {
100102
Evaluator {
101-
env_vars: HashMap::default(),
103+
// `env_vars` could be initialized properly here if `Memory` were available before
104+
// calling this method.
105+
env_vars: EnvVars::default(),
102106
argc: None,
103107
argv: None,
104108
cmd_line: None,
105109
last_error: 0,
106110
tls: TlsData::default(),
111+
communicate,
107112
}
108113
}
109114
}

src/shims/env.rs

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use std::collections::HashMap;
2+
3+
use rustc::ty::layout::{Size, Align};
4+
use rustc_mir::interpret::{Pointer, Memory};
5+
use crate::stacked_borrows::Tag;
6+
use crate::*;
7+
8+
#[derive(Default)]
9+
pub struct EnvVars {
10+
map: HashMap<Vec<u8>, Pointer<Tag>>,
11+
}
12+
13+
impl EnvVars {
14+
pub(crate) fn init<'mir, 'tcx>(
15+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
16+
communicate: bool,
17+
) {
18+
if communicate {
19+
for (name, value) in std::env::vars() {
20+
let value = alloc_env_value(value.as_bytes(), ecx.memory_mut());
21+
ecx.machine.env_vars.map.insert(name.into_bytes(), value);
22+
}
23+
}
24+
}
25+
26+
pub(crate) fn get(&self, name: &[u8]) -> Option<&Pointer<Tag>> {
27+
self.map.get(name)
28+
}
29+
30+
pub(crate) fn unset(&mut self, name: &[u8]) -> Option<Pointer<Tag>> {
31+
self.map.remove(name)
32+
}
33+
34+
pub(crate) fn set(&mut self, name: Vec<u8>, ptr: Pointer<Tag>) -> Option<Pointer<Tag>>{
35+
self.map.insert(name, ptr)
36+
}
37+
}
38+
39+
pub(crate) fn alloc_env_value<'mir, 'tcx>(
40+
bytes: &[u8],
41+
memory: &mut Memory<'mir, 'tcx, Evaluator<'tcx>>,
42+
) -> Pointer<Tag> {
43+
let tcx = {memory.tcx.tcx};
44+
let length = bytes.len() as u64;
45+
// `+1` for the null terminator.
46+
let ptr = memory.allocate(
47+
Size::from_bytes(length + 1),
48+
Align::from_bytes(1).unwrap(),
49+
MiriMemoryKind::Env.into(),
50+
);
51+
// We just allocated these, so the write cannot fail.
52+
let alloc = memory.get_mut(ptr.alloc_id).unwrap();
53+
alloc.write_bytes(&tcx, ptr, &bytes).unwrap();
54+
let trailing_zero_ptr = ptr.offset(
55+
Size::from_bytes(length),
56+
&tcx,
57+
).unwrap();
58+
alloc.write_bytes(&tcx, trailing_zero_ptr, &[0]).unwrap();
59+
ptr
60+
}

src/shims/foreign_items.rs

+4-21
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use syntax::attr;
88
use syntax::symbol::sym;
99

1010
use crate::*;
11+
use crate::shims::env::alloc_env_value;
1112

1213
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
1314
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
@@ -440,7 +441,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
440441
if !this.is_null(name_ptr)? {
441442
let name = this.memory().read_c_str(name_ptr)?.to_owned();
442443
if !name.is_empty() && !name.contains(&b'=') {
443-
success = Some(this.machine.env_vars.remove(&name));
444+
success = Some(this.machine.env_vars.unset(&name));
444445
}
445446
}
446447
}
@@ -468,26 +469,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
468469
}
469470
}
470471
if let Some((name, value)) = new {
471-
// `+1` for the null terminator.
472-
let value_copy = this.memory_mut().allocate(
473-
Size::from_bytes((value.len() + 1) as u64),
474-
Align::from_bytes(1).unwrap(),
475-
MiriMemoryKind::Env.into(),
476-
);
477-
// We just allocated these, so the write cannot fail.
478-
let alloc = this.memory_mut().get_mut(value_copy.alloc_id).unwrap();
479-
alloc.write_bytes(tcx, value_copy, &value).unwrap();
480-
let trailing_zero_ptr = value_copy.offset(
481-
Size::from_bytes(value.len() as u64),
482-
tcx,
483-
).unwrap();
484-
alloc.write_bytes(tcx, trailing_zero_ptr, &[0]).unwrap();
485-
486-
if let Some(var) = this.machine.env_vars.insert(
487-
name.to_owned(),
488-
value_copy,
489-
)
490-
{
472+
let value_copy = alloc_env_value(&value, this.memory_mut());
473+
if let Some(var) = this.machine.env_vars.set(name.to_owned(), value_copy) {
491474
this.memory_mut().deallocate(var, None, MiriMemoryKind::Env.into())?;
492475
}
493476
this.write_null(dest)?;

src/shims/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod foreign_items;
22
pub mod intrinsics;
33
pub mod tls;
44
pub mod dlsym;
5+
pub mod env;
56

67
use rustc::{ty, mir};
78

tests/compiletest.rs

+3
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ fn compile_fail_miri(opt: bool) {
116116
}
117117

118118
fn test_runner(_tests: &[&()]) {
119+
// Add a test env var to do environment communication tests
120+
std::env::set_var("MIRI_ENV_VAR_TEST", "0");
121+
119122
run_pass_miri(false);
120123
run_pass_miri(true);
121124

tests/run-pass/communication.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// ignore-windows: TODO env var emulation stubbed out on Windows
2+
// compile-flags: -Zmiri-enable-communication
3+
4+
fn main() {
5+
assert_eq!(std::env::var("MIRI_ENV_VAR_TEST"), Ok("0".to_owned()));
6+
}

tests/run-pass/env.rs

+2
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ fn main() {
66
assert_eq!(env::var("MIRI_TEST"), Err(env::VarError::NotPresent));
77
env::set_var("MIRI_TEST", "the answer");
88
assert_eq!(env::var("MIRI_TEST"), Ok("the answer".to_owned()));
9+
// Test that miri environment is isolated when communication is disabled.
10+
assert!(env::var("MIRI_ENV_VAR_TEST").is_err());
911
}

0 commit comments

Comments
 (0)