Skip to content

Commit 68a1530

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 49b1947 commit 68a1530

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
@@ -229,19 +229,14 @@ fn main() {
229229
}
230230
"-Zmiri-disable-isolation" => {
231231
miri_config.communicate = true;
232+
miri_config.isolated_op = miri::IsolatedOp::Allow;
232233
}
233234
"-Zmiri-ignore-leaks" => {
234235
miri_config.ignore_leaks = true;
235236
}
236237
"-Zmiri-track-raw-pointers" => {
237238
miri_config.track_raw = true;
238239
}
239-
"-Zmiri-track-dummy-op" => {
240-
miri_config.dummy_op_visibility = miri::DummyOpVisibility::Track;
241-
}
242-
"-Zmiri-ignore-dummy-op" => {
243-
miri_config.dummy_op_visibility = miri::DummyOpVisibility::Hide;
244-
}
245240
"--" => {
246241
after_dashdash = true;
247242
}
@@ -328,6 +323,17 @@ fn main() {
328323
let measureme_out = arg.strip_prefix("-Zmiri-measureme=").unwrap();
329324
miri_config.measureme_out = Some(measureme_out.to_string());
330325
}
326+
arg if arg.starts_with("-Zmiri-isolated-op=") => {
327+
let action = match arg.strip_prefix("-Zmiri-isolated-op=").unwrap() {
328+
"hide" => miri::IsolatedOp::Reject(miri::RejectOpWith::NoWarning),
329+
"warn" => miri::IsolatedOp::Reject(miri::RejectOpWith::Warning),
330+
"warn-nobacktrace" => miri::IsolatedOp::Reject(miri::RejectOpWith::WarningWithoutBacktrace),
331+
"allow" => miri::IsolatedOp::Allow,
332+
_ => panic!("-Zmiri-isolated-op must be `hide`, `warn`, `warn-nobacktrace`, or `allow`"),
333+
334+
};
335+
miri_config.isolated_op = action;
336+
}
331337
_ => {
332338
// Forward to rustc.
333339
rustc_args.push(arg);

src/diagnostics.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,14 @@ pub enum NonHaltingDiagnostic {
4040
CreatedCallId(CallId),
4141
CreatedAlloc(AllocId),
4242
FreedAlloc(AllocId),
43-
DummyOpInIsolation(String),
43+
RejectedIsolatedOp(String),
44+
}
45+
46+
/// Level of Miri specific diagnostics
47+
enum DiagLevel {
48+
Error,
49+
Warning,
50+
Note,
4451
}
4552

4653
/// Emit a custom diagnostic without going through the miri-engine machinery
@@ -120,7 +127,7 @@ pub fn report_error<'tcx, 'mir>(
120127
let msg = e.to_string();
121128
report_msg(
122129
*ecx.tcx,
123-
/*error*/ true,
130+
DiagLevel::Error,
124131
&format!("{}: {}", title, msg),
125132
msg,
126133
helps,
@@ -157,18 +164,19 @@ pub fn report_error<'tcx, 'mir>(
157164
/// Also emits a full stacktrace of the interpreter stack.
158165
fn report_msg<'tcx>(
159166
tcx: TyCtxt<'tcx>,
160-
error: bool,
167+
diag_level: DiagLevel,
161168
title: &str,
162169
span_msg: String,
163170
mut helps: Vec<String>,
164171
stacktrace: &[FrameInfo<'tcx>],
165172
) {
166173
let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);
167-
let mut err = if error {
168-
tcx.sess.struct_span_err(span, title)
169-
} else {
170-
tcx.sess.diagnostic().span_note_diag(span, title)
174+
let mut err = match diag_level {
175+
DiagLevel::Error => tcx.sess.struct_span_err(span, title),
176+
DiagLevel::Warning => tcx.sess.struct_span_warn(span, title),
177+
DiagLevel::Note => tcx.sess.diagnostic().span_note_diag(span, title),
171178
};
179+
172180
// Show main message.
173181
if span != DUMMY_SP {
174182
err.span_label(span, span_msg);
@@ -282,12 +290,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
282290
CreatedCallId(id) => format!("function call with id {}", id),
283291
CreatedAlloc(AllocId(id)) => format!("created allocation with id {}", id),
284292
FreedAlloc(AllocId(id)) => format!("freed allocation with id {}", id),
285-
DummyOpInIsolation(op) => format!("produced dummy error for `{}`", op),
293+
RejectedIsolatedOp(ref op) => format!("`{}` was made to return an error due to isolation", op),
286294
};
295+
296+
let (title, diag_level) = match e {
297+
RejectedIsolatedOp(_) => ("operation rejected by isolation", DiagLevel::Warning),
298+
_ => ("tracking was triggered", DiagLevel::Note),
299+
};
300+
287301
report_msg(
288302
*this.tcx,
289-
/*error*/ false,
290-
"tracking was triggered",
303+
diag_level,
304+
title,
291305
msg,
292306
vec![],
293307
&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.
@@ -43,6 +56,8 @@ pub struct MiriConfig {
4356
pub check_alignment: AlignmentCheck,
4457
/// Determines if communication with the host environment is enabled.
4558
pub communicate: bool,
59+
/// Action for an op requiring communication with the host.
60+
pub isolated_op: IsolatedOp,
4661
/// Determines if memory leaks should be ignored.
4762
pub ignore_leaks: bool,
4863
/// Environment variables that should always be isolated from the host.
@@ -76,6 +91,7 @@ impl Default for MiriConfig {
7691
stacked_borrows: true,
7792
check_alignment: AlignmentCheck::Int,
7893
communicate: false,
94+
isolated_op: IsolatedOp::Reject(RejectOpWith::WarningWithoutBacktrace),
7995
ignore_leaks: false,
8096
excluded_env_vars: vec![],
8197
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
@@ -57,7 +57,7 @@ pub use crate::diagnostics::{
5757
register_diagnostic, report_error, EvalContextExt as DiagnosticsEvalContextExt,
5858
NonHaltingDiagnostic, TerminationInfo,
5959
};
60-
pub use crate::eval::{create_ecx, eval_main, AlignmentCheck, DummyOpVisibility, MiriConfig};
60+
pub use crate::eval::{create_ecx, eval_main, AlignmentCheck, IsolatedOp, MiriConfig, RejectOpWith};
6161
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
6262
pub use crate::machine::{
6363
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,
@@ -312,6 +314,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
312314
cmd_line: None,
313315
tls: TlsData::default(),
314316
communicate: config.communicate,
317+
isolated_op: config.isolated_op,
315318
validate: config.validate,
316319
file_handler: Default::default(),
317320
dir_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)