Skip to content

Commit

Permalink
Add regression test for rustc/rustdoc broken pipe ICEs
Browse files Browse the repository at this point in the history
  • Loading branch information
jieyouxu committed Oct 8, 2024
1 parent 03abf67 commit 5c01316
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 4 deletions.
6 changes: 2 additions & 4 deletions src/bootstrap/src/bin/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,8 @@ fn main() {
cmd.args(lint_flags.split_whitespace());
}

// Conditionally pass `-Zon-broken-pipe=kill` to rustc bin shim when this shim is *not* used to
// build cargo itself, i.e. set `-Zon-broken-pipe=kill` only when building non-cargo tools.
//
// See <https://github.com/rust-lang/rust/issues/131059> for more context.
// Conditionally pass `-Zon-broken-pipe=kill` to underlying rustc. Not all binaries want
// `-Zon-broken-pipe=kill`, which includes cargo itself.
if env::var_os("FORCE_ON_BROKEN_PIPE_KILL").is_some() {
cmd.arg("-Z").arg("on-broken-pipe=kill");
}
Expand Down
73 changes: 73 additions & 0 deletions tests/run-make/broken-pipe-no-ice/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Check that `rustc` and `rustdoc` does not ICE upon encountering a broken pipe due to unhandled
//! panics from raw std `println!` usages.
//!
//! Regression test for <https://github.com/rust-lang/rust/issues/34376>.

//@ ignore-cross-compile (needs to run test binary)

#![feature(anonymous_pipe)]

use std::io::Read;
use std::process::{Command, Stdio};

use run_make_support::env_var;

#[derive(Debug, PartialEq)]
enum Binary {
Rustc,
Rustdoc,
}

fn check_broken_pipe_handled_gracefully(bin: Binary, mut cmd: Command) {
let (reader, writer) = std::pipe::pipe().unwrap();
drop(reader); // close read-end
cmd.stdout(writer).stderr(Stdio::piped());

let mut child = cmd.spawn().unwrap();

let mut stderr = String::new();
child.stderr.as_mut().unwrap().read_to_string(&mut stderr).unwrap();
let status = child.wait().unwrap();

assert!(!status.success(), "{bin:?} unexpectedly succeeded");

const PANIC_ICE_EXIT_CODE: i32 = 101;

#[cfg(not(windows))]
{
// On non-Windows, rustc/rustdoc built with `-Zon-broken-pipe=kill` shouldn't have an exit
// code of 101 because it should have an wait status that corresponds to SIGPIPE signal
// number.
assert_ne!(status.code(), Some(PANIC_ICE_EXIT_CODE), "{bin:?}");
// And the stderr should be empty because rustc/rustdoc should've gotten killed.
assert!(stderr.is_empty(), "{bin:?} stderr:\n{}", stderr);
}

#[cfg(windows)]
{
match bin {
// On Windows, rustc has a paper that propagates the panic exit code of 101 but converts
// broken pipe errors into fatal errors instead of ICEs.
Binary::Rustc => {
assert_eq!(status.code(), Some(PANIC_ICE_EXIT_CODE), "{bin:?}");
// But make sure it doesn't manifest as an ICE.
assert!(!stderr.contains("internal compiler error"), "{bin:?} ICE'd");
}
// On Windows, rustdoc seems to cleanly exit with exit code of 1.
Binary::Rustdoc => {
assert_eq!(status.code(), Some(1), "{bin:?}");
assert!(!stderr.contains("panic"), "{bin:?} stderr contains panic");
}
}
}
}

fn main() {
let mut rustc = Command::new(env_var("RUSTC"));
rustc.arg("--print=sysroot");
check_broken_pipe_handled_gracefully(Binary::Rustc, rustc);

let mut rustdoc = Command::new(env_var("RUSTDOC"));
rustdoc.arg("--version");
check_broken_pipe_handled_gracefully(Binary::Rustdoc, rustdoc);
}

0 comments on commit 5c01316

Please sign in to comment.