about summary refs log tree commit diff
path: root/tests/ui/runtime
diff options
context:
space:
mode:
authorMartin Nordholts <martin.nordholts@codetale.se>2024-04-28 18:02:21 +0200
committerMartin Nordholts <martin.nordholts@codetale.se>2024-05-02 19:48:29 +0200
commitcde0cde151f30e07170d0f3caa721c92bebd7afe (patch)
treef065e316178196507ba419727c37562cdf7f828e /tests/ui/runtime
parente27af2917b80487e9c0de00118fdcb9ccb1177f9 (diff)
downloadrust-cde0cde151f30e07170d0f3caa721c92bebd7afe.tar.gz
rust-cde0cde151f30e07170d0f3caa721c92bebd7afe.zip
Change `SIGPIPE` ui from `#[unix_sigpipe = "..."]` to `-Zon-broken-pipe=...`
In the stabilization attempt of `#[unix_sigpipe = "sig_dfl"]`, a concern
was raised related to using a language attribute for the feature: Long
term, we want `fn lang_start()` to be definable by any crate, not just
libstd. Having a special language attribute in that case becomes
awkward.

So as a first step towards towards the next stabilization attempt, this
PR changes the `#[unix_sigpipe = "..."]` attribute to a compiler flag
`-Zon-broken-pipe=...` to remove that concern, since now the language
is not "contaminated" by this feature.

Another point was also raised, namely that the ui should not leak
**how** it does things, but rather what the **end effect** is. The new
flag uses the proposed naming. This is of course something that can be
iterated on further before stabilization.
Diffstat (limited to 'tests/ui/runtime')
-rw-r--r--tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_dfl.rs6
-rw-r--r--tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_ign.rs6
-rw-r--r--tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs34
-rw-r--r--tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs33
-rw-r--r--tests/ui/runtime/on-broken-pipe/child-processes.rs49
-rw-r--r--tests/ui/runtime/on-broken-pipe/default.rs4
-rw-r--r--tests/ui/runtime/on-broken-pipe/default.stderr2
-rw-r--r--tests/ui/runtime/on-broken-pipe/error.rs10
-rw-r--r--tests/ui/runtime/on-broken-pipe/inherit.rs29
-rw-r--r--tests/ui/runtime/on-broken-pipe/kill.rs11
-rw-r--r--tests/ui/runtime/on-broken-pipe/no-flag-arg.rs4
-rw-r--r--tests/ui/runtime/on-broken-pipe/no-flag-arg.stderr2
-rw-r--r--tests/ui/runtime/on-broken-pipe/not-used.rs9
-rw-r--r--tests/ui/runtime/on-broken-pipe/with-rustc_main.rs14
-rw-r--r--tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs4
-rw-r--r--tests/ui/runtime/on-broken-pipe/wrong-flag-arg.stderr2
16 files changed, 219 insertions, 0 deletions
diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_dfl.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_dfl.rs
new file mode 100644
index 00000000000..b179e484524
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_dfl.rs
@@ -0,0 +1,6 @@
+//@ aux-crate: sigpipe_utils=sigpipe-utils.rs
+//@ compile-flags: -Zon-broken-pipe=inherit
+
+fn main() {
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
+}
diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_ign.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_ign.rs
new file mode 100644
index 00000000000..5ea435521ec
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-inherit-sig_ign.rs
@@ -0,0 +1,6 @@
+//@ aux-crate: sigpipe_utils=sigpipe-utils.rs
+//@ compile-flags: -Zon-broken-pipe=inherit
+
+fn main() {
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
+}
diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs
new file mode 100644
index 00000000000..117f6134b4e
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/auxiliary/assert-sigpipe-disposition.rs
@@ -0,0 +1,34 @@
+// It is UB to unwind out of `fn start()` according to
+// https://doc.rust-lang.org/beta/unstable-book/language-features/start.html so
+// panic with abort to avoid UB:
+//@ compile-flags: -Cpanic=abort
+//@ no-prefer-dynamic so panic=abort works
+
+#![feature(start, rustc_private)]
+
+extern crate libc;
+
+// Use #[start] so we don't have a runtime that messes with SIGPIPE.
+#[start]
+fn start(argc: isize, argv: *const *const u8) -> isize {
+    assert_eq!(argc, 2, "Must pass SIG_IGN or SIG_DFL as first arg");
+    let arg1 = unsafe { std::ffi::CStr::from_ptr(*argv.offset(1) as *const libc::c_char) }
+        .to_str()
+        .unwrap();
+
+    let expected = match arg1 {
+        "SIG_IGN" => libc::SIG_IGN,
+        "SIG_DFL" => libc::SIG_DFL,
+        arg => panic!("Must pass SIG_IGN or SIG_DFL as first arg. Got: {}", arg),
+    };
+
+    let actual = unsafe {
+        let mut actual: libc::sigaction = std::mem::zeroed();
+        libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual);
+        actual.sa_sigaction
+    };
+
+    assert_eq!(actual, expected, "actual and expected SIGPIPE disposition in child differs");
+
+    0
+}
diff --git a/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs b/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs
new file mode 100644
index 00000000000..3d93d50ca3f
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/auxiliary/sigpipe-utils.rs
@@ -0,0 +1,33 @@
+#![feature(rustc_private)]
+extern crate libc;
+
+/// So tests don't have to bring libc in scope themselves
+pub enum SignalHandler {
+    Ignore,
+    Default,
+}
+
+/// Helper to assert that [`libc::SIGPIPE`] has the expected signal handler.
+pub fn assert_sigpipe_handler(expected_handler: SignalHandler) {
+    #[cfg(unix)]
+    #[cfg(not(any(
+        target_os = "emscripten",
+        target_os = "fuchsia",
+        target_os = "horizon",
+        target_os = "android",
+    )))]
+    {
+        let actual = unsafe {
+            let mut actual: libc::sigaction = std::mem::zeroed();
+            libc::sigaction(libc::SIGPIPE, std::ptr::null(), &mut actual);
+            actual.sa_sigaction
+        };
+
+        let expected = match expected_handler {
+            SignalHandler::Ignore => libc::SIG_IGN,
+            SignalHandler::Default => libc::SIG_DFL,
+        };
+
+        assert_eq!(actual, expected, "actual and expected SIGPIPE disposition differs");
+    }
+}
diff --git a/tests/ui/runtime/on-broken-pipe/child-processes.rs b/tests/ui/runtime/on-broken-pipe/child-processes.rs
new file mode 100644
index 00000000000..0da2347481b
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/child-processes.rs
@@ -0,0 +1,49 @@
+//@ revisions: default error kill inherit
+//@ ignore-cross-compile because aux-bin does not yet support it
+//@ only-unix because SIGPIPE is a unix thing
+//@ run-pass
+//@ aux-bin:assert-sigpipe-disposition.rs
+//@ aux-crate:sigpipe_utils=sigpipe-utils.rs
+//@ [kill] compile-flags: -Zunstable-options -Zon-broken-pipe=kill
+//@ [error] compile-flags: -Zunstable-options -Zon-broken-pipe=error
+//@ [inherit] compile-flags: -Zunstable-options -Zon-broken-pipe=inherit
+
+// Checks the signal disposition of `SIGPIPE` in child processes, and in our own
+// process for robustness.
+
+extern crate sigpipe_utils;
+
+use sigpipe_utils::*;
+
+fn main() {
+    // By default we get SIG_IGN but the child gets SIG_DFL through an explicit
+    // reset before exec:
+    // https://github.com/rust-lang/rust/blob/bf4de3a874753bbee3323081c8b0c133444fed2d/library/std/src/sys/pal/unix/process/process_unix.rs#L363-L384
+    #[cfg(default)]
+    let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_DFL");
+
+    // We get SIG_DFL and the child does too without any special code running
+    // before exec.
+    #[cfg(kill)]
+    let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL");
+
+    // We get SIG_IGN and the child does too without any special code running
+    // before exec.
+    #[cfg(error)]
+    let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_IGN");
+
+    // We get SIG_DFL and the child does too without any special code running
+    // before exec.
+    #[cfg(inherit)]
+    let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL");
+
+    assert_sigpipe_handler(we_expect);
+
+    assert!(
+        std::process::Command::new("./auxiliary/bin/assert-sigpipe-disposition")
+            .arg(child_expects)
+            .status()
+            .unwrap()
+            .success()
+    );
+}
diff --git a/tests/ui/runtime/on-broken-pipe/default.rs b/tests/ui/runtime/on-broken-pipe/default.rs
new file mode 100644
index 00000000000..c10d1cfacc0
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/default.rs
@@ -0,0 +1,4 @@
+//@ compile-flags: -Zon-broken-pipe=default
+//@ check-fail
+
+fn main() {}
diff --git a/tests/ui/runtime/on-broken-pipe/default.stderr b/tests/ui/runtime/on-broken-pipe/default.stderr
new file mode 100644
index 00000000000..b90d7566cbb
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/default.stderr
@@ -0,0 +1,2 @@
+error: incorrect value `default` for unstable option `on-broken-pipe` - either `kill`, `error`, or `inherit` was expected
+
diff --git a/tests/ui/runtime/on-broken-pipe/error.rs b/tests/ui/runtime/on-broken-pipe/error.rs
new file mode 100644
index 00000000000..ab2036c2f41
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/error.rs
@@ -0,0 +1,10 @@
+//@ run-pass
+//@ aux-build:sigpipe-utils.rs
+//@ compile-flags: -Zon-broken-pipe=error
+
+fn main() {
+    extern crate sigpipe_utils;
+
+    // `-Zon-broken-pipe=error` is active, so we expect SIGPIPE to be ignored.
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
+}
diff --git a/tests/ui/runtime/on-broken-pipe/inherit.rs b/tests/ui/runtime/on-broken-pipe/inherit.rs
new file mode 100644
index 00000000000..64909b73528
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/inherit.rs
@@ -0,0 +1,29 @@
+//@ ignore-cross-compile because aux-bin does not yet support it
+//@ only-unix because SIGPIPE is a unix thing
+//@ aux-bin: assert-inherit-sig_dfl.rs
+//@ aux-bin: assert-inherit-sig_ign.rs
+//@ run-pass
+//@ compile-flags: -Zon-broken-pipe=kill
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+// By default the Rust runtime resets SIGPIPE to SIG_DFL before exec'ing child
+// processes so opt-out of that with `-Zon-broken-pipe=kill`. See
+// https://github.com/rust-lang/rust/blob/bf4de3a874753bbee3323081c8b0c133444fed2d/library/std/src/sys/pal/unix/process/process_unix.rs#L359-L384
+fn main() {
+    // First expect SIG_DFL in a child process with -`Zon-broken-pipe=inherit`.
+    assert_inherit_sigpipe_disposition("auxiliary/bin/assert-inherit-sig_dfl");
+
+    // With SIG_IGN we expect `-Zon-broken-pipe=inherit` to also get SIG_IGN.
+    unsafe {
+        libc::signal(libc::SIGPIPE, libc::SIG_IGN);
+    }
+    assert_inherit_sigpipe_disposition("auxiliary/bin/assert-inherit-sig_ign");
+}
+
+fn assert_inherit_sigpipe_disposition(aux_bin: &str) {
+    let mut cmd = std::process::Command::new(aux_bin);
+    assert!(cmd.status().unwrap().success());
+}
diff --git a/tests/ui/runtime/on-broken-pipe/kill.rs b/tests/ui/runtime/on-broken-pipe/kill.rs
new file mode 100644
index 00000000000..5dace6f1c6f
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/kill.rs
@@ -0,0 +1,11 @@
+//@ run-pass
+//@ aux-build:sigpipe-utils.rs
+//@ compile-flags: -Zon-broken-pipe=kill
+
+fn main() {
+    extern crate sigpipe_utils;
+
+    // `-Zon-broken-pipe=kill` is active, so SIGPIPE shall NOT be ignored, instead
+    // the default handler shall be installed
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
+}
diff --git a/tests/ui/runtime/on-broken-pipe/no-flag-arg.rs b/tests/ui/runtime/on-broken-pipe/no-flag-arg.rs
new file mode 100644
index 00000000000..2273291bfa7
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/no-flag-arg.rs
@@ -0,0 +1,4 @@
+//@ compile-flags: -Zon-broken-pipe
+//@ check-fail
+
+fn main() {}
diff --git a/tests/ui/runtime/on-broken-pipe/no-flag-arg.stderr b/tests/ui/runtime/on-broken-pipe/no-flag-arg.stderr
new file mode 100644
index 00000000000..3d3e12d303c
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/no-flag-arg.stderr
@@ -0,0 +1,2 @@
+error: unstable option `on-broken-pipe` requires either `kill`, `error`, or `inherit` (Z on-broken-pipe=<value>)
+
diff --git a/tests/ui/runtime/on-broken-pipe/not-used.rs b/tests/ui/runtime/on-broken-pipe/not-used.rs
new file mode 100644
index 00000000000..e31236f2b3d
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/not-used.rs
@@ -0,0 +1,9 @@
+//@ run-pass
+//@ aux-build:sigpipe-utils.rs
+
+fn main() {
+    extern crate sigpipe_utils;
+
+    // SIGPIPE shall be ignored since `-Zon-broken-pipe` is not used
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
+}
diff --git a/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs b/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs
new file mode 100644
index 00000000000..c1731200038
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/with-rustc_main.rs
@@ -0,0 +1,14 @@
+//@ run-pass
+//@ aux-build:sigpipe-utils.rs
+//@ compile-flags: -Zon-broken-pipe=kill
+
+#![feature(rustc_attrs)]
+
+#[rustc_main]
+fn rustc_main() {
+    extern crate sigpipe_utils;
+
+    // `-Zon-broken-pipe=kill` is active, so SIGPIPE handler shall be
+    // SIG_DFL. Note that we have a #[rustc_main], but it should still work.
+    sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
+}
diff --git a/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs b/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs
new file mode 100644
index 00000000000..14d0ac56b5a
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.rs
@@ -0,0 +1,4 @@
+//@ compile-flags: -Zon-broken-pipe=wrong
+//@ check-fail
+
+fn main() {}
diff --git a/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.stderr b/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.stderr
new file mode 100644
index 00000000000..3635418c845
--- /dev/null
+++ b/tests/ui/runtime/on-broken-pipe/wrong-flag-arg.stderr
@@ -0,0 +1,2 @@
+error: incorrect value `wrong` for unstable option `on-broken-pipe` - either `kill`, `error`, or `inherit` was expected
+