Skip to content

Commit 5c01316

Browse files
committed
Add regression test for rustc/rustdoc broken pipe ICEs
1 parent 03abf67 commit 5c01316

File tree

2 files changed

+75
-4
lines changed

2 files changed

+75
-4
lines changed

src/bootstrap/src/bin/rustc.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,8 @@ fn main() {
136136
cmd.args(lint_flags.split_whitespace());
137137
}
138138

139-
// Conditionally pass `-Zon-broken-pipe=kill` to rustc bin shim when this shim is *not* used to
140-
// build cargo itself, i.e. set `-Zon-broken-pipe=kill` only when building non-cargo tools.
141-
//
142-
// See <https://github.com/rust-lang/rust/issues/131059> for more context.
139+
// Conditionally pass `-Zon-broken-pipe=kill` to underlying rustc. Not all binaries want
140+
// `-Zon-broken-pipe=kill`, which includes cargo itself.
143141
if env::var_os("FORCE_ON_BROKEN_PIPE_KILL").is_some() {
144142
cmd.arg("-Z").arg("on-broken-pipe=kill");
145143
}
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//! Check that `rustc` and `rustdoc` does not ICE upon encountering a broken pipe due to unhandled
2+
//! panics from raw std `println!` usages.
3+
//!
4+
//! Regression test for <https://github.com/rust-lang/rust/issues/34376>.
5+
6+
//@ ignore-cross-compile (needs to run test binary)
7+
8+
#![feature(anonymous_pipe)]
9+
10+
use std::io::Read;
11+
use std::process::{Command, Stdio};
12+
13+
use run_make_support::env_var;
14+
15+
#[derive(Debug, PartialEq)]
16+
enum Binary {
17+
Rustc,
18+
Rustdoc,
19+
}
20+
21+
fn check_broken_pipe_handled_gracefully(bin: Binary, mut cmd: Command) {
22+
let (reader, writer) = std::pipe::pipe().unwrap();
23+
drop(reader); // close read-end
24+
cmd.stdout(writer).stderr(Stdio::piped());
25+
26+
let mut child = cmd.spawn().unwrap();
27+
28+
let mut stderr = String::new();
29+
child.stderr.as_mut().unwrap().read_to_string(&mut stderr).unwrap();
30+
let status = child.wait().unwrap();
31+
32+
assert!(!status.success(), "{bin:?} unexpectedly succeeded");
33+
34+
const PANIC_ICE_EXIT_CODE: i32 = 101;
35+
36+
#[cfg(not(windows))]
37+
{
38+
// On non-Windows, rustc/rustdoc built with `-Zon-broken-pipe=kill` shouldn't have an exit
39+
// code of 101 because it should have an wait status that corresponds to SIGPIPE signal
40+
// number.
41+
assert_ne!(status.code(), Some(PANIC_ICE_EXIT_CODE), "{bin:?}");
42+
// And the stderr should be empty because rustc/rustdoc should've gotten killed.
43+
assert!(stderr.is_empty(), "{bin:?} stderr:\n{}", stderr);
44+
}
45+
46+
#[cfg(windows)]
47+
{
48+
match bin {
49+
// On Windows, rustc has a paper that propagates the panic exit code of 101 but converts
50+
// broken pipe errors into fatal errors instead of ICEs.
51+
Binary::Rustc => {
52+
assert_eq!(status.code(), Some(PANIC_ICE_EXIT_CODE), "{bin:?}");
53+
// But make sure it doesn't manifest as an ICE.
54+
assert!(!stderr.contains("internal compiler error"), "{bin:?} ICE'd");
55+
}
56+
// On Windows, rustdoc seems to cleanly exit with exit code of 1.
57+
Binary::Rustdoc => {
58+
assert_eq!(status.code(), Some(1), "{bin:?}");
59+
assert!(!stderr.contains("panic"), "{bin:?} stderr contains panic");
60+
}
61+
}
62+
}
63+
}
64+
65+
fn main() {
66+
let mut rustc = Command::new(env_var("RUSTC"));
67+
rustc.arg("--print=sysroot");
68+
check_broken_pipe_handled_gracefully(Binary::Rustc, rustc);
69+
70+
let mut rustdoc = Command::new(env_var("RUSTDOC"));
71+
rustdoc.arg("--version");
72+
check_broken_pipe_handled_gracefully(Binary::Rustdoc, rustdoc);
73+
}

0 commit comments

Comments
 (0)