summaryrefslogtreecommitdiffstats
path: root/tests/ui/issues/issue-30490.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /tests/ui/issues/issue-30490.rs
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/ui/issues/issue-30490.rs')
-rw-r--r--tests/ui/issues/issue-30490.rs102
1 files changed, 102 insertions, 0 deletions
diff --git a/tests/ui/issues/issue-30490.rs b/tests/ui/issues/issue-30490.rs
new file mode 100644
index 000000000..4f0eeac8f
--- /dev/null
+++ b/tests/ui/issues/issue-30490.rs
@@ -0,0 +1,102 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-fuchsia Child I/O swaps not privileged
+
+// Previously libstd would set stdio descriptors of a child process
+// by `dup`ing the requested descriptors to inherit directly into the
+// stdio descriptors. This, however, would incorrectly handle cases
+// where the descriptors to inherit were already stdio descriptors.
+// This test checks to avoid that regression.
+
+#![cfg_attr(unix, feature(rustc_private))]
+#![cfg_attr(not(unix), allow(unused_imports))]
+
+#[cfg(unix)]
+extern crate libc;
+
+use std::fs::File;
+use std::io::{Read, Write};
+use std::io::{stdout, stderr};
+use std::process::{Command, Stdio};
+
+#[cfg(unix)]
+use std::os::unix::io::FromRawFd;
+
+#[cfg(not(unix))]
+fn main() {
+ // Bug not present in Windows
+}
+
+#[cfg(unix)]
+fn main() {
+ let mut args = std::env::args();
+ let name = args.next().unwrap();
+ let args: Vec<String> = args.collect();
+ if let Some("--child") = args.get(0).map(|s| &**s) {
+ return child();
+ } else if !args.is_empty() {
+ panic!("unknown options");
+ }
+
+ let stdout_backup = unsafe { libc::dup(libc::STDOUT_FILENO) };
+ let stderr_backup = unsafe { libc::dup(libc::STDERR_FILENO) };
+ assert!(stdout_backup > -1);
+ assert!(stderr_backup > -1);
+
+ let (stdout_reader, stdout_writer) = pipe();
+ let (stderr_reader, stderr_writer) = pipe();
+ assert!(unsafe { libc::dup2(stdout_writer, libc::STDOUT_FILENO) } > -1);
+ assert!(unsafe { libc::dup2(stderr_writer, libc::STDERR_FILENO) } > -1);
+
+ // Make sure we close any duplicates of the writer end of the pipe,
+ // otherwise we can get stuck reading from the pipe which has open
+ // writers but no one supplying any input
+ assert_eq!(unsafe { libc::close(stdout_writer) }, 0);
+ assert_eq!(unsafe { libc::close(stderr_writer) }, 0);
+
+ stdout().write_all("parent stdout\n".as_bytes()).expect("failed to write to stdout");
+ stderr().write_all("parent stderr\n".as_bytes()).expect("failed to write to stderr");
+
+ let child = {
+ Command::new(name)
+ .arg("--child")
+ .stdin(Stdio::inherit())
+ .stdout(unsafe { Stdio::from_raw_fd(libc::STDERR_FILENO) })
+ .stderr(unsafe { Stdio::from_raw_fd(libc::STDOUT_FILENO) })
+ .spawn()
+ };
+
+ // The Stdio passed into the Command took over (and closed) std{out, err}
+ // so we should restore them as they were.
+ assert!(unsafe { libc::dup2(stdout_backup, libc::STDOUT_FILENO) } > -1);
+ assert!(unsafe { libc::dup2(stderr_backup, libc::STDERR_FILENO) } > -1);
+
+ // Using File as a shim around the descriptor
+ let mut read = String::new();
+ let mut f: File = unsafe { FromRawFd::from_raw_fd(stdout_reader) };
+ f.read_to_string(&mut read).expect("failed to read from stdout file");
+ assert_eq!(read, "parent stdout\nchild stderr\n");
+
+ // Using File as a shim around the descriptor
+ read.clear();
+ let mut f: File = unsafe { FromRawFd::from_raw_fd(stderr_reader) };
+ f.read_to_string(&mut read).expect("failed to read from stderr file");
+ assert_eq!(read, "parent stderr\nchild stdout\n");
+
+ assert!(child.expect("failed to execute child process").wait().unwrap().success());
+}
+
+#[cfg(unix)]
+fn child() {
+ stdout().write_all("child stdout\n".as_bytes()).expect("child failed to write to stdout");
+ stderr().write_all("child stderr\n".as_bytes()).expect("child failed to write to stderr");
+}
+
+#[cfg(unix)]
+/// Returns a pipe (reader, writer combo)
+fn pipe() -> (i32, i32) {
+ let mut fds = [0; 2];
+ assert_eq!(unsafe { libc::pipe(fds.as_mut_ptr()) }, 0);
+ (fds[0], fds[1])
+}