Skip to content

Commit 9b9c548

Browse files
committed
Add -Zmiri-env-set to set environment variables without modifying the host environment
This option allows to pass environment variables to the interpreted program without needing to modify the host environment (which may have undesired effects in some cases).
1 parent 95ae2dd commit 9b9c548

File tree

5 files changed

+43
-12
lines changed

5 files changed

+43
-12
lines changed

src/tools/miri/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,10 @@ environment variable. We first document the most relevant and most commonly used
311311
* `-Zmiri-env-forward=<var>` forwards the `var` environment variable to the interpreted program. Can
312312
be used multiple times to forward several variables. Execution will still be deterministic if the
313313
value of forwarded variables stays the same. Has no effect if `-Zmiri-disable-isolation` is set.
314+
* `-Zmiri-env-set=<var>=<value>` sets the `var` environment variable to `value` in the interpreted.
315+
It can be used to pass environment variables without needing to alter the host environment. It can
316+
be used multiple times to set several variables. If `-Zmiri-disable-isolation` or `-Zmiri-env-forward`
317+
is set, values set with this option will have priority over values from the host environment.
314318
* `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some
315319
remaining threads to exist when the main thread exits.
316320
* `-Zmiri-isolation-error=<action>` configures Miri's response to operations

src/tools/miri/src/bin/miri.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,11 @@ fn main() {
498498
);
499499
} else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") {
500500
miri_config.forwarded_env_vars.push(param.to_owned());
501+
} else if let Some(param) = arg.strip_prefix("-Zmiri-env-set=") {
502+
let Some((name, value)) = param.split_once('=') else {
503+
show_error!("-Zmiri-env-set requires an argument of the form <name>=<value>");
504+
};
505+
miri_config.set_env_vars.insert(name.to_owned(), value.to_owned());
501506
} else if let Some(param) = arg.strip_prefix("-Zmiri-track-pointer-tag=") {
502507
let ids: Vec<u64> = match parse_comma_list(param) {
503508
Ok(ids) => ids,

src/tools/miri/src/eval.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::thread;
99

1010
use crate::concurrency::thread::TlsAllocAction;
1111
use crate::diagnostics::report_leaks;
12-
use rustc_data_structures::fx::FxHashSet;
12+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1313
use rustc_hir::def::Namespace;
1414
use rustc_hir::def_id::DefId;
1515
use rustc_middle::ty::{
@@ -100,6 +100,8 @@ pub struct MiriConfig {
100100
pub ignore_leaks: bool,
101101
/// Environment variables that should always be forwarded from the host.
102102
pub forwarded_env_vars: Vec<String>,
103+
/// Additional environment variables that should be set in the interpreted program.
104+
pub set_env_vars: FxHashMap<String, String>,
103105
/// Command-line arguments passed to the interpreted program.
104106
pub args: Vec<String>,
105107
/// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
@@ -163,6 +165,7 @@ impl Default for MiriConfig {
163165
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
164166
ignore_leaks: false,
165167
forwarded_env_vars: vec![],
168+
set_env_vars: FxHashMap::default(),
166169
args: vec![],
167170
seed: None,
168171
tracked_pointer_tags: FxHashSet::default(),

src/tools/miri/src/shims/env.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,15 @@ impl<'tcx> EnvVars<'tcx> {
4444
let forward = ecx.machine.communicate()
4545
|| config.forwarded_env_vars.iter().any(|v| **v == *name);
4646
if forward {
47-
let var_ptr = match ecx.tcx.sess.target.os.as_ref() {
48-
_ if ecx.target_os_is_unix() =>
49-
alloc_env_var_as_c_str(name.as_ref(), value.as_ref(), ecx)?,
50-
"windows" => alloc_env_var_as_wide_str(name.as_ref(), value.as_ref(), ecx)?,
51-
unsupported =>
52-
throw_unsup_format!(
53-
"environment support for target OS `{}` not yet available",
54-
unsupported
55-
),
56-
};
57-
ecx.machine.env_vars.map.insert(name.clone(), var_ptr);
47+
add_env_var(ecx, name, value)?;
5848
}
5949
}
6050
}
6151

52+
for (name, value) in &config.set_env_vars {
53+
add_env_var(ecx, OsStr::new(name), OsStr::new(value))?;
54+
}
55+
6256
// Initialize the `environ` pointer when needed.
6357
if ecx.target_os_is_unix() {
6458
// This is memory backing an extern static, hence `ExternStatic`, not `Env`.
@@ -89,6 +83,24 @@ impl<'tcx> EnvVars<'tcx> {
8983
}
9084
}
9185

86+
fn add_env_var<'mir, 'tcx>(
87+
ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
88+
name: &OsStr,
89+
value: &OsStr,
90+
) -> InterpResult<'tcx, ()> {
91+
let var_ptr = match ecx.tcx.sess.target.os.as_ref() {
92+
_ if ecx.target_os_is_unix() => alloc_env_var_as_c_str(name, value, ecx)?,
93+
"windows" => alloc_env_var_as_wide_str(name, value, ecx)?,
94+
unsupported =>
95+
throw_unsup_format!(
96+
"environment support for target OS `{}` not yet available",
97+
unsupported
98+
),
99+
};
100+
ecx.machine.env_vars.map.insert(name.to_os_string(), var_ptr);
101+
Ok(())
102+
}
103+
92104
fn alloc_env_var_as_c_str<'mir, 'tcx>(
93105
name: &OsStr,
94106
value: &OsStr,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Test a value set on the host (MIRI_ENV_VAR_TEST) and one that is not.
2+
//@compile-flags: -Zmiri-env-set=MIRI_ENV_VAR_TEST=test_value_1 -Zmiri-env-set=TEST_VAR_2=test_value_2
3+
4+
fn main() {
5+
assert_eq!(std::env::var("MIRI_ENV_VAR_TEST"), Ok("test_value_1".to_owned()));
6+
assert_eq!(std::env::var("TEST_VAR_2"), Ok("test_value_2".to_owned()));
7+
}

0 commit comments

Comments
 (0)