Skip to content

Commit ed2104b

Browse files
committed
Consolidated settings for isolated ops
In user interface, added a new option `-Zmiri-isolated-op` which takes one of the four values -- hide, warn, warn-nobacktrace, and allow. This option can be used to both disable isolation (allow), and to control warning levels if op is rejected due to isolation. In implementation, added a new enum `IsolatedOp` to capture all the settings related to ops requiring communication with the host. Existing `communicate` flag in both miri configs and machine state will be discarded once its use is replaced with the new enum. Added a new helper function to report rejected ops in isolation according to the warning settings. Refactored miri specific diagnostics function `report_msg` to take an enum value instead of a bool, indicating the level of diagnostics. Updated shims related to current dir to use the new enum. Added a .stderr file for the new test, matching the warning thrown.
1 parent af8c2a0 commit ed2104b

File tree

8 files changed

+155
-95
lines changed

8 files changed

+155
-95
lines changed

src/bin/miri.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -292,19 +292,14 @@ fn main() {
292292
}
293293
"-Zmiri-disable-isolation" => {
294294
miri_config.communicate = true;
295+
miri_config.isolated_op = miri::IsolatedOp::Allow;
295296
}
296297
"-Zmiri-ignore-leaks" => {
297298
miri_config.ignore_leaks = true;
298299
}
299300
"-Zmiri-track-raw-pointers" => {
300301
miri_config.track_raw = true;
301302
}
302-
"-Zmiri-track-dummy-op" => {
303-
miri_config.dummy_op_visibility = miri::DummyOpVisibility::Track;
304-
}
305-
"-Zmiri-ignore-dummy-op" => {
306-
miri_config.dummy_op_visibility = miri::DummyOpVisibility::Hide;
307-
}
308303
"--" => {
309304
after_dashdash = true;
310305
}
@@ -391,6 +386,17 @@ fn main() {
391386
let measureme_out = arg.strip_prefix("-Zmiri-measureme=").unwrap();
392387
miri_config.measureme_out = Some(measureme_out.to_string());
393388
}
389+
arg if arg.starts_with("-Zmiri-isolated-op=") => {
390+
let action = match arg.strip_prefix("-Zmiri-isolated-op=").unwrap() {
391+
"hide" => miri::IsolatedOp::Reject(miri::RejectOpWith::NoWarning),
392+
"warn" => miri::IsolatedOp::Reject(miri::RejectOpWith::Warning),
393+
"warn-nobacktrace" => miri::IsolatedOp::Reject(miri::RejectOpWith::WarningWithoutBacktrace),
394+
"allow" => miri::IsolatedOp::Allow,
395+
_ => panic!("-Zmiri-isolated-op must be `hide`, `warn`, `warn-nobacktrace`, or `allow`"),
396+
397+
};
398+
miri_config.isolated_op = action;
399+
}
394400
_ => {
395401
// Forward to rustc.
396402
rustc_args.push(arg);

src/diagnostics.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ pub enum NonHaltingDiagnostic {
5252
CreatedCallId(CallId),
5353
CreatedAlloc(AllocId),
5454
FreedAlloc(AllocId),
55-
DummyOpInIsolation(String),
55+
RejectedIsolatedOp(String),
56+
}
57+
58+
/// Level of Miri specific diagnostics
59+
enum DiagLevel {
60+
Error,
61+
Warning,
62+
Note,
5663
}
5764

5865
/// Emit a custom diagnostic without going through the miri-engine machinery
@@ -138,7 +145,7 @@ pub fn report_error<'tcx, 'mir>(
138145
let msg = e.to_string();
139146
report_msg(
140147
*ecx.tcx,
141-
/*error*/ true,
148+
DiagLevel::Error,
142149
&if let Some(title) = title { format!("{}: {}", title, msg) } else { msg.clone() },
143150
msg,
144151
helps,
@@ -175,18 +182,19 @@ pub fn report_error<'tcx, 'mir>(
175182
/// Also emits a full stacktrace of the interpreter stack.
176183
fn report_msg<'tcx>(
177184
tcx: TyCtxt<'tcx>,
178-
error: bool,
185+
diag_level: DiagLevel,
179186
title: &str,
180187
span_msg: String,
181188
mut helps: Vec<(Option<SpanData>, String)>,
182189
stacktrace: &[FrameInfo<'tcx>],
183190
) {
184191
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
185-
let mut err = if error {
186-
tcx.sess.struct_span_err(span, title)
187-
} else {
188-
tcx.sess.diagnostic().span_note_diag(span, title)
192+
let mut err = match diag_level {
193+
DiagLevel::Error => tcx.sess.struct_span_err(span, title),
194+
DiagLevel::Warning => tcx.sess.struct_span_warn(span, title),
195+
DiagLevel::Note => tcx.sess.diagnostic().span_note_diag(span, title),
189196
};
197+
190198
// Show main message.
191199
if span != DUMMY_SP {
192200
err.span_label(span, span_msg);
@@ -304,12 +312,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
304312
CreatedCallId(id) => format!("function call with id {}", id),
305313
CreatedAlloc(AllocId(id)) => format!("created allocation with id {}", id),
306314
FreedAlloc(AllocId(id)) => format!("freed allocation with id {}", id),
307-
DummyOpInIsolation(op) => format!("produced dummy error for `{}`", op),
315+
RejectedIsolatedOp(ref op) => format!("`{}` was made to return an error due to isolation", op),
308316
};
317+
318+
let (title, diag_level) = match e {
319+
RejectedIsolatedOp(_) => ("operation rejected by isolation", DiagLevel::Warning),
320+
_ => ("tracking was triggered", DiagLevel::Note),
321+
};
322+
309323
report_msg(
310324
*this.tcx,
311-
/*error*/ false,
312-
"tracking was triggered",
325+
diag_level,
326+
title,
313327
msg,
314328
vec![],
315329
&stacktrace,

src/eval.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,26 @@ pub enum AlignmentCheck {
2323
}
2424

2525
#[derive(Copy, Clone, Debug)]
26-
pub enum DummyOpVisibility {
27-
/// Do not report a dummy op
28-
Hide,
29-
/// Print a warning without a backtrace, once per dummy op
30-
Warn,
31-
/// Print a note with a backtrace, once per line calling dummy op
32-
Track,
26+
pub enum RejectOpWith {
27+
/// Do not print warning about rejected isolated op
28+
NoWarning,
29+
30+
/// Print a warning about rejected isolated op, with backtrace
31+
Warning,
32+
33+
/// Print a warning about rejected isolated op, without backtrace
34+
WarningWithoutBacktrace,
35+
}
36+
37+
#[derive(Copy, Clone, Debug)]
38+
pub enum IsolatedOp {
39+
/// Reject op requiring communication with the host. Usually, miri
40+
/// generates a fake error for such op, and prints a warning
41+
/// about it. Warning levels are controlled by `RejectOpWith` enum.
42+
Reject(RejectOpWith),
43+
44+
/// Execute op requiring communication with the host, i.e. disable isolation.
45+
Allow,
3346
}
3447

3548
/// Configuration needed to spawn a Miri instance.
@@ -45,6 +58,8 @@ pub struct MiriConfig {
4558
pub check_abi: bool,
4659
/// Determines if communication with the host environment is enabled.
4760
pub communicate: bool,
61+
/// Action for an op requiring communication with the host.
62+
pub isolated_op: IsolatedOp,
4863
/// Determines if memory leaks should be ignored.
4964
pub ignore_leaks: bool,
5065
/// Environment variables that should always be isolated from the host.
@@ -79,6 +94,7 @@ impl Default for MiriConfig {
7994
check_alignment: AlignmentCheck::Int,
8095
check_abi: true,
8196
communicate: false,
97+
isolated_op: IsolatedOp::Reject(RejectOpWith::WarningWithoutBacktrace),
8298
ignore_leaks: false,
8399
excluded_env_vars: vec![],
84100
args: vec![],

src/helpers.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -397,23 +397,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
397397
Ok(())
398398
}
399399

400-
/// Helper function used inside the shims of foreign functions which produce a dummy error
401-
/// when isolation is enabled. It is used to print a warning/backtrace, informing this
402-
/// decision to the user.
403-
fn report_dummy_in_isolation(&self, name: &str) {
400+
/// Helper function used inside the shims of foreign functions which reject the op
401+
/// when isolation is enabled. It is used to print a warning/backtrace about the rejection.
402+
fn report_rejected_op(&self, op_name: &str, reject_with: RejectOpWith) {
404403
let this = self.eval_context_ref();
405-
match this.machine.dummy_op_visibility {
406-
DummyOpVisibility::Warn => {
407-
let msg = format!("`{}` in isolation mode produced a dummy error", name);
404+
match reject_with {
405+
RejectOpWith::WarningWithoutBacktrace => {
406+
let msg = format!("`{}` was made to return an error due to isolation", op_name);
408407
let mut warn = this.tcx.sess.struct_warn(&msg);
409-
warn.note("run with -Zmiri-track-dummy-op to track with a backtrace");
410-
warn.note("run with -Zmiri-ignore-dummy-op to ignore warning");
408+
warn.note("run with -Zmiri-isolated-op=warn to get warning with a backtrace");
409+
warn.note("run with -Zmiri-isolated-op=hide to hide warning");
410+
warn.note("run with -Zmiri-isolated-op=allow to disable isolation");
411411
warn.emit();
412412
}
413-
DummyOpVisibility::Track => {
414-
register_diagnostic(NonHaltingDiagnostic::DummyOpInIsolation(name.to_string()));
413+
RejectOpWith::Warning => {
414+
register_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
415415
}
416-
DummyOpVisibility::Hide => {} // no diagnostics
416+
RejectOpWith::NoWarning => {} // no warning
417417
}
418418
}
419419

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub use crate::diagnostics::{
5959
register_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt,
6060
NonHaltingDiagnostic, TerminationInfo,
6161
};
62-
pub use crate::eval::{create_ecx, eval_main, AlignmentCheck, DummyOpVisibility, MiriConfig};
62+
pub use crate::eval::{create_ecx, eval_main, AlignmentCheck, IsolatedOp, MiriConfig, RejectOpWith};
6363
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
6464
pub use crate::machine::{
6565
AllocExtra, Evaluator, FrameData, MemoryExtra, MiriEvalContext, MiriEvalContextExt,

src/machine.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,10 @@ pub struct Evaluator<'mir, 'tcx> {
267267
/// and random number generation is delegated to the host.
268268
pub(crate) communicate: bool,
269269

270-
/// Level of user visibility to dummy ops run in isolation mode.
271-
pub(crate) dummy_op_visibility: DummyOpVisibility,
270+
/// What should Miri do when an op requires communicating with the host,
271+
/// such as accessing host env vars, random number generation, and
272+
/// file system access.
273+
pub(crate) isolated_op: IsolatedOp,
272274

273275
/// Whether to enforce the validity invariant.
274276
pub(crate) validate: bool,
@@ -318,6 +320,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
318320
cmd_line: None,
319321
tls: TlsData::default(),
320322
communicate: config.communicate,
323+
isolated_op: config.isolated_op,
321324
validate: config.validate,
322325
enforce_abi: config.check_abi,
323326
file_handler: Default::default(),

src/shims/env.rs

Lines changed: 66 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -322,25 +322,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
322322
"`getcwd` is only available for the UNIX target family"
323323
);
324324

325-
if this.machine.communicate {
326-
let buf = this.read_scalar(&buf_op)?.check_init()?;
327-
let size = this.read_scalar(&size_op)?.to_machine_usize(&*this.tcx)?;
328-
// If we cannot get the current directory, we return null
329-
match env::current_dir() {
330-
Ok(cwd) => {
331-
if this.write_path_to_c_str(&cwd, buf, size)?.0 {
332-
return Ok(buf);
325+
match this.machine.isolated_op {
326+
IsolatedOp::Allow => {
327+
let buf = this.read_scalar(&buf_op)?.check_init()?;
328+
let size = this.read_scalar(&size_op)?.to_machine_usize(&*this.tcx)?;
329+
// If we cannot get the current directory, we return null
330+
match env::current_dir() {
331+
Ok(cwd) => {
332+
if this.write_path_to_c_str(&cwd, buf, size)?.0 {
333+
return Ok(buf);
334+
}
335+
let erange = this.eval_libc("ERANGE")?;
336+
this.set_last_error(erange)?;
333337
}
334-
let erange = this.eval_libc("ERANGE")?;
335-
this.set_last_error(erange)?;
338+
Err(e) => this.set_last_error_from_io_error(e)?,
336339
}
337-
Err(e) => this.set_last_error_from_io_error(e)?,
338340
}
339-
} else {
340-
// Return a dummy error in isolation mode, after printing a warning about it.
341-
this.report_dummy_in_isolation("getcwd");
342-
let err = Error::new(ErrorKind::NotFound, "dummy error in isolation mode");
343-
this.set_last_error_from_io_error(err)?;
341+
IsolatedOp::Reject(reject_with) => {
342+
let err = Error::new(ErrorKind::NotFound, "rejected due to isolation");
343+
this.set_last_error_from_io_error(err)?;
344+
this.report_rejected_op("getcwd", reject_with);
345+
}
344346
}
345347
Ok(Scalar::null_ptr(&*this.tcx))
346348
}
@@ -353,22 +355,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
353355
) -> InterpResult<'tcx, u32> {
354356
let this = self.eval_context_mut();
355357
this.assert_target_os("windows", "GetCurrentDirectoryW");
356-
357-
if this.machine.communicate {
358-
let size = u64::from(this.read_scalar(size_op)?.to_u32()?);
359-
let buf = this.read_scalar(buf_op)?.check_init()?;
360-
361-
// If we cannot get the current directory, we return 0
362-
match env::current_dir() {
363-
Ok(cwd) =>
364-
return Ok(windows_check_buffer_size(this.write_path_to_wide_str(&cwd, buf, size)?)),
365-
Err(e) => this.set_last_error_from_io_error(e)?,
358+
359+
match this.machine.isolated_op {
360+
IsolatedOp::Allow => {
361+
let size = u64::from(this.read_scalar(size_op)?.to_u32()?);
362+
let buf = this.read_scalar(buf_op)?.check_init()?;
363+
364+
// If we cannot get the current directory, we return 0
365+
match env::current_dir() {
366+
Ok(cwd) =>
367+
return Ok(windows_check_buffer_size(this.write_path_to_wide_str(&cwd, buf, size)?)),
368+
Err(e) => this.set_last_error_from_io_error(e)?,
369+
}
370+
}
371+
IsolatedOp::Reject(reject_with) => {
372+
let err = Error::new(ErrorKind::NotFound, "rejected due to isolation");
373+
this.set_last_error_from_io_error(err)?;
374+
this.report_rejected_op("GetCurrentDirectoryW", reject_with);
366375
}
367-
} else {
368-
// Return a dummy error in isolation mode, after printing a warning about it.
369-
this.report_dummy_in_isolation("GetCurrentDirectoryW");
370-
let err = Error::new(ErrorKind::NotFound, "dummy error in isolation mode");
371-
this.set_last_error_from_io_error(err)?;
372376
}
373377
Ok(0)
374378
}
@@ -381,23 +385,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
381385
"`getcwd` is only available for the UNIX target family"
382386
);
383387

384-
if this.machine.communicate {
385-
let path = this.read_path_from_c_str(this.read_scalar(path_op)?.check_init()?)?;
388+
match this.machine.isolated_op {
389+
IsolatedOp::Allow => {
390+
let path = this.read_path_from_c_str(this.read_scalar(path_op)?.check_init()?)?;
386391

387-
match env::set_current_dir(path) {
388-
Ok(()) => Ok(0),
389-
Err(e) => {
390-
this.set_last_error_from_io_error(e)?;
391-
Ok(-1)
392+
match env::set_current_dir(path) {
393+
Ok(()) => Ok(0),
394+
Err(e) => {
395+
this.set_last_error_from_io_error(e)?;
396+
Ok(-1)
397+
}
392398
}
393399
}
394-
} else {
395-
// Return a dummy error in isolation mode, after printing a warning about it.
396-
this.report_dummy_in_isolation("chdir");
397-
let err = Error::new(ErrorKind::NotFound, "dummy error in isolation mode");
398-
this.set_last_error_from_io_error(err)?;
400+
IsolatedOp::Reject(reject_with) => {
401+
let err = Error::new(ErrorKind::NotFound, "rejected due to isolation");
402+
this.set_last_error_from_io_error(err)?;
403+
this.report_rejected_op("chdir", reject_with);
399404

400-
Ok(-1)
405+
Ok(-1)
406+
}
401407
}
402408
}
403409

@@ -411,22 +417,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
411417
let this = self.eval_context_mut();
412418
this.assert_target_os("windows", "SetCurrentDirectoryW");
413419

414-
if this.machine.communicate {
415-
let path = this.read_path_from_wide_str(this.read_scalar(path_op)?.check_init()?)?;
420+
match this.machine.isolated_op {
421+
IsolatedOp::Allow => {
422+
let path = this.read_path_from_wide_str(this.read_scalar(path_op)?.check_init()?)?;
416423

417-
match env::set_current_dir(path) {
418-
Ok(()) => Ok(1),
419-
Err(e) => {
420-
this.set_last_error_from_io_error(e)?;
421-
Ok(0)
424+
match env::set_current_dir(path) {
425+
Ok(()) => Ok(1),
426+
Err(e) => {
427+
this.set_last_error_from_io_error(e)?;
428+
Ok(0)
429+
}
422430
}
423431
}
424-
} else {
425-
// Return a dummy error in isolation mode, after printing a warning about it.
426-
this.report_dummy_in_isolation("SetCurrentDirectoryW");
427-
let err = Error::new(ErrorKind::NotFound, "dummy error in isolation mode");
428-
this.set_last_error_from_io_error(err)?;
429-
Ok(0)
432+
IsolatedOp::Reject(reject_with) => {
433+
let err = Error::new(ErrorKind::NotFound, "rejected due to isolation");
434+
this.set_last_error_from_io_error(err)?;
435+
this.report_rejected_op("SetCurrentDirectoryW", reject_with);
436+
437+
Ok(0)
438+
}
430439
}
431440
}
432441

0 commit comments

Comments
 (0)