diff --git a/README.md b/README.md index f0e14a9125..2e24402450 100644 --- a/README.md +++ b/README.md @@ -228,6 +228,11 @@ environment variable: This can be used to find which parts of your program are executing slowly under Miri. The profile is written out to a file with the prefix ``, and can be processed using the tools in the repository https://github.com/rust-lang/measureme. +* `-Zmiri-panic-on-unsupported` will makes some forms of unsupported functionality, + such as FFI and unsupported syscalls, panic within the context of the emulated + application instead of raising an error within the context of Miri (and halting + execution). Note that code might not expect these operations to ever panic, so + this flag can lead to strange (mis)behavior. * `-Zmiri-seed=` configures the seed of the RNG that Miri uses to resolve non-determinism. This RNG is used to pick base addresses for allocations. When isolation is enabled (the default), this is also used to emulate system diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 514e5b0aeb..7fd6d85cff 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -296,6 +296,9 @@ fn main() { "-Zmiri-ignore-leaks" => { miri_config.ignore_leaks = true; } + "-Zmiri-panic-on-unsupported" => { + miri_config.panic_on_unsupported = true; + } "-Zmiri-track-raw-pointers" => { miri_config.track_raw = true; } diff --git a/src/eval.rs b/src/eval.rs index 6646783d34..8d5876d777 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -59,6 +59,8 @@ pub struct MiriConfig { /// If `Some`, enable the `measureme` profiler, writing results to a file /// with the specified prefix. pub measureme_out: Option, + /// Panic when unsupported functionality is encountered + pub panic_on_unsupported: bool, } impl Default for MiriConfig { @@ -80,6 +82,7 @@ impl Default for MiriConfig { data_race_detector: true, cmpxchg_weak_failure_rate: 0.8, measureme_out: None, + panic_on_unsupported: false, } } } diff --git a/src/helpers.rs b/src/helpers.rs index 5217634205..c8e1d6c880 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -636,6 +636,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx == this.tcx.def_path(start_fn).krate }) } + + /// Handler that should be called when unsupported functionality is encountered. + /// This function will either panic within the context of the emulated application + /// or return an error in the Miri process context + /// + /// Return value of `Ok(bool)` indicates whether execution should continue. + fn handle_unsupported>(&mut self, error_msg: S) -> InterpResult<'tcx, ()> { + let this = self.eval_context_mut(); + if this.machine.panic_on_unsupported { + // message is slightly different here to make automated analysis easier + let error_msg = format!("unsupported Miri functionality: {}", error_msg.as_ref()); + this.start_panic(error_msg.as_ref(), StackPopUnwind::Skip)?; + return Ok(()); + } else { + throw_unsup_format!("{}", error_msg.as_ref()); + } + } } /// Check that the number of args is what we expect. diff --git a/src/machine.rs b/src/machine.rs index 9c49575ded..f25c1b9720 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -297,6 +297,11 @@ pub struct Evaluator<'mir, 'tcx> { /// Cache of `Instance` exported under the given `Symbol` name. pub(crate) exported_symbols_cache: FxHashMap>, + + /// Whether to raise a panic in the context of the evaluated process when unsupported + /// functionality is encountered. If `false`, an error is propagated in the Miri application context + /// instead (default behavior) + pub(crate) panic_on_unsupported: bool, } impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { @@ -326,6 +331,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { profiler, string_cache: Default::default(), exported_symbols_cache: FxHashMap::default(), + panic_on_unsupported: config.panic_on_unsupported, } } } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index c838c136c3..8f3dfdc4f8 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -259,7 +259,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx if let Some(body) = this.lookup_exported_symbol(link_name_sym)? { return Ok(Some(body)); } - throw_unsup_format!("can't call (diverging) foreign function: {}", link_name); + this.handle_unsupported(format!( + "can't call (diverging) foreign function: {}", + link_name + ))?; + return Ok(None); } }, Some(p) => p, @@ -276,7 +280,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx if let Some(body) = this.lookup_exported_symbol(link_name_sym)? { return Ok(Some(body)); } - throw_unsup_format!("can't call foreign function: {}", link_name); + + this.handle_unsupported(format!("can't call foreign function: {}", link_name))?; + return Ok(None); } } diff --git a/src/shims/panic.rs b/src/shims/panic.rs index b1da7f340f..15620c73f0 100644 --- a/src/shims/panic.rs +++ b/src/shims/panic.rs @@ -161,7 +161,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx } } - /// Starta a panic in the interpreter with the given message as payload. + /// Start a panic in the interpreter with the given message as payload. fn start_panic(&mut self, msg: &str, unwind: StackPopUnwind) -> InterpResult<'tcx> { let this = self.eval_context_mut(); diff --git a/src/shims/posix/linux/foreign_items.rs b/src/shims/posix/linux/foreign_items.rs index 9017dc368b..68ae704fb0 100644 --- a/src/shims/posix/linux/foreign_items.rs +++ b/src/shims/posix/linux/foreign_items.rs @@ -183,7 +183,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx id if id == sys_futex => { futex(this, args, dest)?; } - id => throw_unsup_format!("Miri does not support syscall ID {}", id), + id => { + this.handle_unsupported(format!("can't execute syscall with ID {}", id))?; + return Ok(EmulateByNameResult::NotSupported); + } } } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 76657a5355..02f9bb8fff 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -343,7 +343,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx // Better error for attempts to create a thread "CreateThread" => { this.check_abi(abi, Abi::System { unwind: false })?; - throw_unsup_format!("Miri does not support concurrency on Windows"); + + this.handle_unsupported("can't create threads on Windows")?; + return Ok(EmulateByNameResult::AlreadyJumped); } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. diff --git a/tests/compile-fail/concurrency/thread-spawn.rs b/tests/compile-fail/concurrency/thread-spawn.rs index 27760eed8d..6f07d083a0 100644 --- a/tests/compile-fail/concurrency/thread-spawn.rs +++ b/tests/compile-fail/concurrency/thread-spawn.rs @@ -3,7 +3,7 @@ use std::thread; -// error-pattern: Miri does not support concurrency on Windows +// error-pattern: can't create threads on Windows fn main() { thread::spawn(|| {}); diff --git a/tests/run-pass/panic/unsupported_foreign_function.rs b/tests/run-pass/panic/unsupported_foreign_function.rs new file mode 100644 index 0000000000..bc3d02c5f2 --- /dev/null +++ b/tests/run-pass/panic/unsupported_foreign_function.rs @@ -0,0 +1,11 @@ +// compile-flags: -Zmiri-panic-on-unsupported + +fn main() { + extern "Rust" { + fn foo(); + } + + unsafe { + foo(); + } +} diff --git a/tests/run-pass/panic/unsupported_foreign_function.stderr b/tests/run-pass/panic/unsupported_foreign_function.stderr new file mode 100644 index 0000000000..bd7f3490d8 --- /dev/null +++ b/tests/run-pass/panic/unsupported_foreign_function.stderr @@ -0,0 +1,2 @@ +thread 'main' panicked at 'unsupported Miri functionality: can't call foreign function: foo', $DIR/unsupported_foreign_function.rs:9:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace