diff --git a/compiler/miri/cargo-miri-playground b/compiler/miri/cargo-miri-playground
index 98781de1e..d3302941b 100755
--- a/compiler/miri/cargo-miri-playground
+++ b/compiler/miri/cargo-miri-playground
@@ -3,5 +3,4 @@
set -eu
export MIRI_SYSROOT=~/.cache/miri/HOST
-export MIRIFLAGS="-Zmiri-disable-isolation"
exec cargo miri run
diff --git a/ui/frontend/Help.tsx b/ui/frontend/Help.tsx
index 48e7c35a6..ab860aef6 100644
--- a/ui/frontend/Help.tsx
+++ b/ui/frontend/Help.tsx
@@ -37,7 +37,8 @@ const CLIPPY_EXAMPLE = `fn main() {
}
}`;
-const MIRI_EXAMPLE = `fn main() {
+const MIRI_EXAMPLE = `// MIRIFLAGS=-Zmiri-disable-isolation
+fn main() {
let mut a: [u8; 0] = [];
unsafe {
*a.get_unchecked_mut(1) = 1;
@@ -154,7 +155,9 @@ const Help: React.SFC = () => {
Miri is an interpreter for Rust’s mid-level intermediate
representation (MIR) and can be used to detect certain kinds of undefined behavior
in your unsafe Rust code. Click on the Miri button in
- the Tools menu to check.
+ the Tools menu to check. When running code with Miri, if the first
+ source line is a comment following a bash-like syntax for setting environment
+ variables, it will be used to set the MIRIFLAGS
environment variable.
diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs
index c77daebba..4bff06d76 100644
--- a/ui/src/sandbox.rs
+++ b/ui/src/sandbox.rs
@@ -1,3 +1,4 @@
+use regex::Regex;
use serde_derive::Deserialize;
use snafu::{ResultExt, Snafu};
use std::{ffi::OsStr, fmt, io, os::unix::fs::PermissionsExt, string, time::Duration};
@@ -280,6 +281,27 @@ fn set_execution_environment(
cmd.apply_backtrace(&req);
}
+fn parse_miri_flags(code: &str) -> Option {
+ lazy_static::lazy_static! {
+ pub static ref MIRIFLAGS: Regex = Regex::new(r#"(///|//!|//)\s*MIRIFLAGS\s*=\s*(.*)"#).unwrap();
+ }
+
+ fn match_of(line: &str, pat: &Regex) -> Option {
+ pat.captures(line)
+ .and_then(|caps| caps.get(2))
+ .map(|mat| mat.as_str().trim_matches(&['\'', '"'][..]).to_string())
+ }
+
+ for line in code.trim().lines() {
+ let line = line.trim();
+ if let Some(miri_flags) = match_of(line, &*MIRIFLAGS) {
+ return Some(miri_flags);
+ }
+ }
+
+ None
+}
+
pub mod fut {
use snafu::prelude::*;
use std::{
@@ -292,9 +314,9 @@ pub mod fut {
use tokio::{fs, process::Command, time};
use super::{
- basic_secure_docker_command, build_execution_command, set_execution_environment,
- vec_to_str, wide_open_permissions, BacktraceRequest, Channel, ClippyRequest,
- ClippyResponse, CompileRequest, CompileResponse, CompileTarget,
+ basic_secure_docker_command, build_execution_command, parse_miri_flags,
+ set_execution_environment, vec_to_str, wide_open_permissions, BacktraceRequest, Channel,
+ ClippyRequest, ClippyResponse, CompileRequest, CompileResponse, CompileTarget,
CompilerExecutionTimedOutSnafu, CrateInformation, CrateInformationInner, CrateType,
CrateTypeRequest, DemangleAssembly, DockerCommandExt, EditionRequest, ExecuteRequest,
ExecuteResponse, FormatRequest, FormatResponse, MacroExpansionRequest,
@@ -454,7 +476,9 @@ pub mod fut {
pub async fn miri(&self, req: &MiriRequest) -> Result {
self.write_source_code(&req.code).await?;
- let command = self.miri_command(req);
+
+ let miri_flags = parse_miri_flags(&req.code);
+ let command = self.miri_command(miri_flags, req);
let output = run_command_with_timeout(command).await?;
@@ -651,10 +675,14 @@ pub mod fut {
cmd
}
- fn miri_command(&self, req: impl EditionRequest) -> Command {
+ fn miri_command(&self, miri_flags: Option, req: impl EditionRequest) -> Command {
let mut cmd = self.docker_command(None);
cmd.apply_edition(req);
+ if let Some(flags) = miri_flags {
+ cmd.args(&["--env", &format!("MIRIFLAGS={}", flags)]);
+ }
+
cmd.arg("miri").args(&["cargo", "miri-playground"]);
log::debug!("Miri command is {:?}", cmd);
@@ -1636,17 +1664,114 @@ mod test {
assert!(
resp.stderr
- .contains("pointer must be in-bounds at offset 1"),
+ .contains("has size 0, so pointer to 1 byte starting at offset 0 is out-of-bounds"),
"was: {}",
resp.stderr
);
+ Ok(())
+ }
+
+ #[test]
+ fn magic_comments() {
+ let _singleton = one_test_at_a_time();
+
+ let expected = Some("-Zmiri-tag-raw-pointers".to_string());
+
+ // Every comment syntax
+ let code = r#"//MIRIFLAGS=-Zmiri-tag-raw-pointers"#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ let code = r#"///MIRIFLAGS=-Zmiri-tag-raw-pointers"#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ let code = r#"//!MIRIFLAGS=-Zmiri-tag-raw-pointers"#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ // Whitespace is ignored
+ let code = r#"// MIRIFLAGS = -Zmiri-tag-raw-pointers"#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ // Both quotes are stripped
+ let code = r#"//MIRIFLAGS='-Zmiri-tag-raw-pointers'"#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ let code = r#"//MIRIFLAGS="-Zmiri-tag-raw-pointers""#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ // Leading code is ignored
+ let code = r#"
+fn main() {}
+//MIRIFLAGS=-Zmiri-tag-raw-pointers"#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ // Leading whitespace is ignored, flags still parse
+ let code = r#" //MIRIFLAGS=-Zmiri-tag-raw-pointers"#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ let expected = Some("-Zmiri-tag-raw-pointers -Zmiri-symbolic-alignment-check".to_string());
+
+ // Doesn't break up flags even if they contain whitespace
+ let code = r#"//MIRIFLAGS="-Zmiri-tag-raw-pointers -Zmiri-symbolic-alignment-check""#;
+ assert_eq!(parse_miri_flags(code), expected);
+
+ // Even if they aren't wrapped in quoted (possibly dubious)
+ let code = r#"//MIRIFLAGS=-Zmiri-tag-raw-pointers -Zmiri-symbolic-alignment-check"#;
+ assert_eq!(parse_miri_flags(code), expected);
+ }
+
+ #[test]
+ fn miriflags_work() -> Result<()> {
+ let _singleton = one_test_at_a_time();
+
+ let code = r#"
+ fn main() {
+ let a = 0u8;
+ let ptr = &a as *const u8 as usize as *const u8;
+ unsafe {
+ println!("{}", *ptr);
+ }
+ }
+ "#;
+
+ let req = MiriRequest {
+ code: code.to_string(),
+ edition: None,
+ };
+
+ let sb = Sandbox::new()?;
+ let resp = sb.miri(&req)?;
+
+ assert!(resp.success);
+
+ let code = r#"
+ // MIRIFLAGS=-Zmiri-tag-raw-pointers
+ fn main() {
+ let a = 0u8;
+ let ptr = &a as *const u8 as usize as *const u8;
+ unsafe {
+ println!("{}", *ptr);
+ }
+ }
+ "#;
+
+ let req = MiriRequest {
+ code: code.to_string(),
+ edition: None,
+ };
+
+ let sb = Sandbox::new()?;
+ let resp = sb.miri(&req)?;
+
+ assert!(!resp.success);
+
assert!(
- resp.stderr.contains("outside bounds of alloc"),
+ resp.stderr
+ .contains("trying to reborrow for SharedReadOnly"),
"was: {}",
resp.stderr
);
assert!(
- resp.stderr.contains("which has size 0"),
+ resp.stderr.contains("but parent tag"),
"was: {}",
resp.stderr
);