Skip to content

Commit dde4408

Browse files
authored
Merge pull request #1002 from rust-lang/miri
Install and prepare Miri in the primary container
2 parents f14e3a8 + c9a730c commit dde4408

File tree

8 files changed

+323
-180
lines changed

8 files changed

+323
-180
lines changed

compiler/base/Dockerfile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- \
4646
--default-toolchain "${channel}" \
4747
--target wasm32-unknown-unknown \
4848
--component rustfmt \
49-
--component clippy
49+
--component clippy \
50+
--component rust-src
51+
RUN if [ "${channel}" = 'nightly' ]; then rustup component add miri; fi
5052

5153
COPY --chown=playground entrypoint.sh /playground/tools/
5254

@@ -111,11 +113,13 @@ ARG channel
111113
RUN cargo build
112114
RUN cargo build --release
113115
RUN cargo clippy
116+
RUN if [ "${channel}" = 'nightly' ]; then cargo miri setup; cargo miri run; fi
114117
RUN rm src/*.rs
115118

116119
COPY --from=modify-cargo-toml /playground/modify-cargo-toml/target/release/modify-cargo-toml /playground/.cargo/bin
117120
COPY --from=build-orchestrator /playground/.cargo/bin/worker /playground/.cargo/bin/worker
118121
COPY --from=wasm-tools /playground/.cargo/bin/wasm-tools /playground/.cargo/bin
119-
COPY --chown=playground cargo-wasm /playground/.cargo/bin/
122+
COPY --chown=playground cargo-wasm /playground/.cargo/bin
123+
COPY --chown=playground cargo-miri-playground /playground/.cargo/bin
120124

121125
ENTRYPOINT ["/playground/tools/entrypoint.sh"]

compiler/base/cargo-miri-playground

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
3+
set -eu
4+
5+
export MIRI_SYSROOT=~/.cache/miri
6+
export MIRIFLAGS="-Zmiri-disable-isolation"
7+
exec cargo miri run

compiler/base/orchestrator/src/coordinator.rs

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,54 @@ pub struct ClippyResponse {
458458
pub exit_detail: String,
459459
}
460460

461+
#[derive(Debug, Clone)]
462+
pub struct MiriRequest {
463+
pub channel: Channel,
464+
pub crate_type: CrateType,
465+
pub edition: Edition,
466+
pub code: String,
467+
}
468+
469+
impl MiriRequest {
470+
pub(crate) fn delete_previous_main_request(&self) -> DeleteFileRequest {
471+
delete_previous_primary_file_request(self.crate_type)
472+
}
473+
474+
pub(crate) fn write_main_request(&self) -> WriteFileRequest {
475+
write_primary_file_request(self.crate_type, &self.code)
476+
}
477+
478+
pub(crate) fn execute_cargo_request(&self) -> ExecuteCommandRequest {
479+
ExecuteCommandRequest {
480+
cmd: "cargo".to_owned(),
481+
args: vec!["miri-playground".to_owned()],
482+
envs: Default::default(),
483+
cwd: None,
484+
}
485+
}
486+
}
487+
488+
impl CargoTomlModifier for MiriRequest {
489+
fn modify_cargo_toml(&self, mut cargo_toml: toml::Value) -> toml::Value {
490+
if self.edition == Edition::Rust2024 {
491+
cargo_toml = modify_cargo_toml::set_feature_edition2024(cargo_toml);
492+
}
493+
494+
cargo_toml = modify_cargo_toml::set_edition(cargo_toml, self.edition.to_cargo_toml_key());
495+
496+
if let Some(crate_type) = self.crate_type.to_library_cargo_toml_key() {
497+
cargo_toml = modify_cargo_toml::set_crate_type(cargo_toml, crate_type);
498+
}
499+
cargo_toml
500+
}
501+
}
502+
503+
#[derive(Debug, Clone)]
504+
pub struct MiriResponse {
505+
pub success: bool,
506+
pub exit_detail: String,
507+
}
508+
461509
#[derive(Debug, Clone)]
462510
pub struct WithOutput<T> {
463511
pub response: T,
@@ -649,6 +697,30 @@ where
649697
.await
650698
}
651699

700+
pub async fn miri(&self, request: MiriRequest) -> Result<WithOutput<MiriResponse>, MiriError> {
701+
use miri_error::*;
702+
703+
self.select_channel(request.channel)
704+
.await
705+
.context(CouldNotStartContainerSnafu)?
706+
.miri(request)
707+
.await
708+
}
709+
710+
pub async fn begin_miri(
711+
&self,
712+
token: CancellationToken,
713+
request: MiriRequest,
714+
) -> Result<ActiveMiri, MiriError> {
715+
use miri_error::*;
716+
717+
self.select_channel(request.channel)
718+
.await
719+
.context(CouldNotStartContainerSnafu)?
720+
.begin_miri(token, request)
721+
.await
722+
}
723+
652724
pub async fn idle(&mut self) -> Result<()> {
653725
let Self {
654726
stable,
@@ -1076,6 +1148,75 @@ impl Container {
10761148
})
10771149
}
10781150

1151+
async fn miri(&self, request: MiriRequest) -> Result<WithOutput<MiriResponse>, MiriError> {
1152+
let token = Default::default();
1153+
1154+
let ActiveMiri {
1155+
task,
1156+
stdout_rx,
1157+
stderr_rx,
1158+
} = self.begin_miri(token, request).await?;
1159+
1160+
WithOutput::try_absorb(task, stdout_rx, stderr_rx).await
1161+
}
1162+
1163+
async fn begin_miri(
1164+
&self,
1165+
token: CancellationToken,
1166+
request: MiriRequest,
1167+
) -> Result<ActiveMiri, MiriError> {
1168+
use miri_error::*;
1169+
1170+
let delete_previous_main = request.delete_previous_main_request();
1171+
let write_main = request.write_main_request();
1172+
let execute_cargo = request.execute_cargo_request();
1173+
1174+
let delete_previous_main = self.commander.one(delete_previous_main);
1175+
let write_main = self.commander.one(write_main);
1176+
let modify_cargo_toml = self.modify_cargo_toml.modify_for(&request);
1177+
1178+
let (delete_previous_main, write_main, modify_cargo_toml) =
1179+
join!(delete_previous_main, write_main, modify_cargo_toml);
1180+
1181+
delete_previous_main.context(CouldNotDeletePreviousCodeSnafu)?;
1182+
write_main.context(CouldNotWriteCodeSnafu)?;
1183+
modify_cargo_toml.context(CouldNotModifyCargoTomlSnafu)?;
1184+
1185+
let SpawnCargo {
1186+
task,
1187+
stdin_tx,
1188+
stdout_rx,
1189+
stderr_rx,
1190+
} = self
1191+
.spawn_cargo_task(token, execute_cargo)
1192+
.await
1193+
.context(CouldNotStartCargoSnafu)?;
1194+
1195+
drop(stdin_tx);
1196+
1197+
let task = async move {
1198+
let ExecuteCommandResponse {
1199+
success,
1200+
exit_detail,
1201+
} = task
1202+
.await
1203+
.context(CargoTaskPanickedSnafu)?
1204+
.context(CargoFailedSnafu)?;
1205+
1206+
Ok(MiriResponse {
1207+
success,
1208+
exit_detail,
1209+
})
1210+
}
1211+
.boxed();
1212+
1213+
Ok(ActiveMiri {
1214+
task,
1215+
stdout_rx,
1216+
stderr_rx,
1217+
})
1218+
}
1219+
10791220
async fn spawn_cargo_task(
10801221
&self,
10811222
token: CancellationToken,
@@ -1346,6 +1487,47 @@ pub enum ClippyError {
13461487
CargoFailed { source: SpawnCargoError },
13471488
}
13481489

1490+
pub struct ActiveMiri {
1491+
pub task: BoxFuture<'static, Result<MiriResponse, MiriError>>,
1492+
pub stdout_rx: mpsc::Receiver<String>,
1493+
pub stderr_rx: mpsc::Receiver<String>,
1494+
}
1495+
1496+
impl fmt::Debug for ActiveMiri {
1497+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1498+
f.debug_struct("ActiveMiri")
1499+
.field("task", &"<future>")
1500+
.field("stdout_rx", &self.stdout_rx)
1501+
.field("stderr_rx", &self.stderr_rx)
1502+
.finish()
1503+
}
1504+
}
1505+
1506+
#[derive(Debug, Snafu)]
1507+
#[snafu(module)]
1508+
pub enum MiriError {
1509+
#[snafu(display("Could not start the container"))]
1510+
CouldNotStartContainer { source: Error },
1511+
1512+
#[snafu(display("Could not modify Cargo.toml"))]
1513+
CouldNotModifyCargoToml { source: ModifyCargoTomlError },
1514+
1515+
#[snafu(display("Could not delete previous source code"))]
1516+
CouldNotDeletePreviousCode { source: CommanderError },
1517+
1518+
#[snafu(display("Could not write source code"))]
1519+
CouldNotWriteCode { source: CommanderError },
1520+
1521+
#[snafu(display("Could not start Cargo task"))]
1522+
CouldNotStartCargo { source: SpawnCargoError },
1523+
1524+
#[snafu(display("The Cargo task panicked"))]
1525+
CargoTaskPanicked { source: tokio::task::JoinError },
1526+
1527+
#[snafu(display("Cargo task failed"))]
1528+
CargoFailed { source: SpawnCargoError },
1529+
}
1530+
13491531
struct SpawnCargo {
13501532
task: JoinHandle<Result<ExecuteCommandResponse, SpawnCargoError>>,
13511533
stdin_tx: mpsc::Sender<String>,
@@ -2910,6 +3092,43 @@ mod tests {
29103092
Ok(())
29113093
}
29123094

3095+
const ARBITRARY_MIRI_REQUEST: MiriRequest = MiriRequest {
3096+
channel: Channel::Nightly,
3097+
crate_type: CrateType::Binary,
3098+
edition: Edition::Rust2021,
3099+
code: String::new(),
3100+
};
3101+
3102+
#[tokio::test]
3103+
#[snafu::report]
3104+
async fn miri() -> Result<()> {
3105+
// cargo-miri-playground only exists inside the container
3106+
let coordinator = new_coordinator_docker().await;
3107+
3108+
let req = MiriRequest {
3109+
code: r#"
3110+
fn main() {
3111+
let mut a: [u8; 0] = [];
3112+
unsafe { *a.get_unchecked_mut(1) = 1; }
3113+
}
3114+
"#
3115+
.into(),
3116+
..ARBITRARY_MIRI_REQUEST
3117+
};
3118+
3119+
let response = coordinator.miri(req).with_timeout().await.unwrap();
3120+
3121+
assert!(!response.success, "stderr: {}", response.stderr);
3122+
3123+
assert_contains!(response.stderr, "Undefined Behavior");
3124+
assert_contains!(response.stderr, "pointer to 1 byte");
3125+
assert_contains!(response.stderr, "starting at offset 0");
3126+
assert_contains!(response.stderr, "is out-of-bounds");
3127+
assert_contains!(response.stderr, "has size 0");
3128+
3129+
Ok(())
3130+
}
3131+
29133132
// The next set of tests are broader than the functionality of a
29143133
// single operation.
29153134

compiler/miri/Dockerfile

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
ARG base_image=shepmaster/rust-nightly
22
FROM ${base_image}
33

4-
RUN rustup component add rust-src miri
5-
6-
RUN echo 'fn main() {}' > src/main.rs
7-
RUN cargo miri setup
8-
RUN cargo miri run
9-
RUN rm src/*.rs
10-
11-
ADD --chown=playground cargo-miri-playground /playground/.cargo/bin
4+
# The base image takes care of all this for now
125

136
ENTRYPOINT ["/playground/tools/entrypoint.sh"]

ui/src/main.rs

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,6 @@ enum Error {
167167
SandboxCreation { source: sandbox::Error },
168168
#[snafu(display("Expansion operation failed: {}", source))]
169169
Expansion { source: sandbox::Error },
170-
#[snafu(display("Interpreting operation failed: {}", source))]
171-
Interpreting { source: sandbox::Error },
172170
#[snafu(display("Caching operation failed: {}", source))]
173171
Caching { source: sandbox::Error },
174172
#[snafu(display("Gist creation failed: {}", source))]
@@ -207,6 +205,11 @@ enum Error {
207205
source: server_axum::api_orchestrator_integration_impls::ParseClippyRequestError,
208206
},
209207

208+
#[snafu(context(false))]
209+
MiriRequest {
210+
source: server_axum::api_orchestrator_integration_impls::ParseMiriRequestError,
211+
},
212+
210213
// Remove at a later point. From here ...
211214
#[snafu(display("The value {:?} is not a valid edition", value))]
212215
InvalidEdition { value: String },
@@ -248,6 +251,11 @@ enum Error {
248251
source: orchestrator::coordinator::ClippyError,
249252
},
250253

254+
#[snafu(display("Unable to convert the Miri request"))]
255+
Miri {
256+
source: orchestrator::coordinator::MiriError,
257+
},
258+
251259
#[snafu(display("The operation timed out"))]
252260
Timeout { source: tokio::time::error::Elapsed },
253261

@@ -377,6 +385,7 @@ struct MiriRequest {
377385
#[derive(Debug, Clone, Serialize)]
378386
struct MiriResponse {
379387
success: bool,
388+
exit_detail: String,
380389
stdout: String,
381390
stderr: String,
382391
}
@@ -443,27 +452,6 @@ struct EvaluateResponse {
443452
error: Option<String>,
444453
}
445454

446-
impl TryFrom<MiriRequest> for sandbox::MiriRequest {
447-
type Error = Error;
448-
449-
fn try_from(me: MiriRequest) -> Result<Self> {
450-
Ok(sandbox::MiriRequest {
451-
code: me.code,
452-
edition: parse_edition(&me.edition)?,
453-
})
454-
}
455-
}
456-
457-
impl From<sandbox::MiriResponse> for MiriResponse {
458-
fn from(me: sandbox::MiriResponse) -> Self {
459-
MiriResponse {
460-
success: me.success,
461-
stdout: me.stdout,
462-
stderr: me.stderr,
463-
}
464-
}
465-
}
466-
467455
impl TryFrom<MacroExpansionRequest> for sandbox::MacroExpansionRequest {
468456
type Error = Error;
469457

0 commit comments

Comments
 (0)