about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/bootstrap/src/bin/rustc.rs6
-rw-r--r--tests/run-make/broken-pipe-no-ice/rmake.rs73
2 files changed, 75 insertions, 4 deletions
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index 6f7ccde5e5b..18f5a1a58db 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -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");
     }
diff --git a/tests/run-make/broken-pipe-no-ice/rmake.rs b/tests/run-make/broken-pipe-no-ice/rmake.rs
new file mode 100644
index 00000000000..d1db0bc7368
--- /dev/null
+++ b/tests/run-make/broken-pipe-no-ice/rmake.rs
@@ -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);
+}