summaryrefslogtreecommitdiffstats
path: root/src/test/ui/process
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /src/test/ui/process
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/ui/process')
-rw-r--r--src/test/ui/process/core-run-destroy.rs87
-rw-r--r--src/test/ui/process/fds-are-cloexec.rs82
-rw-r--r--src/test/ui/process/issue-13304.rs39
-rw-r--r--src/test/ui/process/issue-14456.rs37
-rw-r--r--src/test/ui/process/issue-14940.rs19
-rw-r--r--src/test/ui/process/issue-16272.rs23
-rw-r--r--src/test/ui/process/issue-20091.rs24
-rw-r--r--src/test/ui/process/multi-panic.rs38
-rw-r--r--src/test/ui/process/no-stdio.rs139
-rw-r--r--src/test/ui/process/nofile-limit.rs46
-rw-r--r--src/test/ui/process/process-envs.rs53
-rw-r--r--src/test/ui/process/process-exit.rs26
-rw-r--r--src/test/ui/process/process-panic-after-fork.rs151
-rw-r--r--src/test/ui/process/process-remove-from-env.rs47
-rw-r--r--src/test/ui/process/process-sigpipe.rs36
-rw-r--r--src/test/ui/process/process-spawn-nonexistent.rs16
-rw-r--r--src/test/ui/process/process-spawn-with-unicode-params.rs76
-rw-r--r--src/test/ui/process/process-status-inherits-stdin.rs35
-rw-r--r--src/test/ui/process/signal-exit-status.rs18
-rw-r--r--src/test/ui/process/sigpipe-should-be-ignored.rs33
-rw-r--r--src/test/ui/process/tls-exit-status.rs11
-rw-r--r--src/test/ui/process/try-wait.rs60
22 files changed, 1096 insertions, 0 deletions
diff --git a/src/test/ui/process/core-run-destroy.rs b/src/test/ui/process/core-run-destroy.rs
new file mode 100644
index 000000000..5fd418e6c
--- /dev/null
+++ b/src/test/ui/process/core-run-destroy.rs
@@ -0,0 +1,87 @@
+// run-pass
+
+#![allow(unused_must_use)]
+#![allow(stable_features)]
+#![allow(deprecated)]
+#![allow(unused_imports)]
+// compile-flags:--test
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-vxworks no 'cat' and 'sleep'
+
+// N.B., these tests kill child processes. Valgrind sees these children as leaking
+// memory, which makes for some *confusing* logs. That's why these are here
+// instead of in std.
+
+#![feature(rustc_private, duration)]
+
+extern crate libc;
+
+use std::process::{self, Command, Child, Output, Stdio};
+use std::str;
+use std::sync::mpsc::channel;
+use std::thread;
+use std::time::Duration;
+
+macro_rules! t {
+ ($e:expr) => (match $e { Ok(e) => e, Err(e) => panic!("error: {}", e) })
+}
+
+#[test]
+fn test_destroy_once() {
+ let mut p = sleeper();
+ t!(p.kill());
+}
+
+#[cfg(unix)]
+pub fn sleeper() -> Child {
+ t!(Command::new("sleep").arg("1000").spawn())
+}
+#[cfg(windows)]
+pub fn sleeper() -> Child {
+ // There's a `timeout` command on windows, but it doesn't like having
+ // its output piped, so instead just ping ourselves a few times with
+ // gaps in between so we're sure this process is alive for awhile
+ t!(Command::new("ping").arg("127.0.0.1").arg("-n").arg("1000").spawn())
+}
+
+#[test]
+fn test_destroy_twice() {
+ let mut p = sleeper();
+ t!(p.kill()); // this shouldn't crash...
+ let _ = p.kill(); // ...and nor should this (and nor should the destructor)
+}
+
+#[test]
+fn test_destroy_actually_kills() {
+ let cmd = if cfg!(windows) {
+ "cmd"
+ } else if cfg!(target_os = "android") {
+ "/system/bin/cat"
+ } else {
+ "cat"
+ };
+
+ // this process will stay alive indefinitely trying to read from stdin
+ let mut p = t!(Command::new(cmd)
+ .stdin(Stdio::piped())
+ .spawn());
+
+ t!(p.kill());
+
+ // Don't let this test time out, this should be quick
+ let (tx, rx) = channel();
+ thread::spawn(move|| {
+ thread::sleep_ms(1000);
+ if rx.try_recv().is_err() {
+ process::exit(1);
+ }
+ });
+ let code = t!(p.wait()).code();
+ if cfg!(windows) {
+ assert!(code.is_some());
+ } else {
+ assert!(code.is_none());
+ }
+ tx.send(());
+}
diff --git a/src/test/ui/process/fds-are-cloexec.rs b/src/test/ui/process/fds-are-cloexec.rs
new file mode 100644
index 000000000..4482b7032
--- /dev/null
+++ b/src/test/ui/process/fds-are-cloexec.rs
@@ -0,0 +1,82 @@
+// run-pass
+// ignore-windows
+// ignore-android
+// ignore-emscripten no processes
+// ignore-haiku
+// ignore-sgx no processes
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::env;
+use std::fs::File;
+use std::io;
+use std::net::{TcpListener, TcpStream, UdpSocket};
+use std::os::unix::prelude::*;
+use std::process::{Command, Stdio};
+use std::thread;
+
+fn main() {
+ let args = env::args().collect::<Vec<_>>();
+ if args.len() == 1 {
+ parent()
+ } else {
+ child(&args)
+ }
+}
+
+fn parent() {
+ let file = File::open(env::current_exe().unwrap()).unwrap();
+ let tcp1 = TcpListener::bind("127.0.0.1:0").unwrap();
+ let tcp2 = tcp1.try_clone().unwrap();
+ let addr = tcp1.local_addr().unwrap();
+ let t = thread::spawn(move || TcpStream::connect(addr).unwrap());
+ let tcp3 = tcp1.accept().unwrap().0;
+ let tcp4 = t.join().unwrap();
+ let tcp5 = tcp3.try_clone().unwrap();
+ let tcp6 = tcp4.try_clone().unwrap();
+ let udp1 = UdpSocket::bind("127.0.0.1:0").unwrap();
+ let udp2 = udp1.try_clone().unwrap();
+
+ let mut child = Command::new(env::args().next().unwrap())
+ .arg("100")
+ .stdout(Stdio::piped())
+ .stdin(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn().unwrap();
+ let pipe1 = child.stdin.take().unwrap();
+ let pipe2 = child.stdout.take().unwrap();
+ let pipe3 = child.stderr.take().unwrap();
+
+
+ let status = Command::new(env::args().next().unwrap())
+ .arg(file.as_raw_fd().to_string())
+ .arg(tcp1.as_raw_fd().to_string())
+ .arg(tcp2.as_raw_fd().to_string())
+ .arg(tcp3.as_raw_fd().to_string())
+ .arg(tcp4.as_raw_fd().to_string())
+ .arg(tcp5.as_raw_fd().to_string())
+ .arg(tcp6.as_raw_fd().to_string())
+ .arg(udp1.as_raw_fd().to_string())
+ .arg(udp2.as_raw_fd().to_string())
+ .arg(pipe1.as_raw_fd().to_string())
+ .arg(pipe2.as_raw_fd().to_string())
+ .arg(pipe3.as_raw_fd().to_string())
+ .status()
+ .unwrap();
+ assert!(status.success());
+ child.wait().unwrap();
+}
+
+fn child(args: &[String]) {
+ let mut b = [0u8; 2];
+ for arg in &args[1..] {
+ let fd: libc::c_int = arg.parse().unwrap();
+ unsafe {
+ assert_eq!(libc::read(fd, b.as_mut_ptr() as *mut _, 2), -1);
+ assert_eq!(io::Error::last_os_error().raw_os_error(),
+ Some(libc::EBADF));
+ }
+ }
+}
diff --git a/src/test/ui/process/issue-13304.rs b/src/test/ui/process/issue-13304.rs
new file mode 100644
index 000000000..b10f6d572
--- /dev/null
+++ b/src/test/ui/process/issue-13304.rs
@@ -0,0 +1,39 @@
+// run-pass
+#![allow(unused_mut)]
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::env;
+use std::io::prelude::*;
+use std::io;
+use std::process::{Command, Stdio};
+use std::str;
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ if args.len() > 1 && args[1] == "child" {
+ child();
+ } else {
+ parent();
+ }
+}
+
+fn parent() {
+ let args: Vec<String> = env::args().collect();
+ let mut p = Command::new(&args[0]).arg("child")
+ .stdout(Stdio::piped())
+ .stdin(Stdio::piped())
+ .spawn().unwrap();
+ p.stdin.as_mut().unwrap().write_all(b"test1\ntest2\ntest3").unwrap();
+ let out = p.wait_with_output().unwrap();
+ assert!(out.status.success());
+ let s = str::from_utf8(&out.stdout).unwrap();
+ assert_eq!(s, "test1\ntest2\ntest3\n");
+}
+
+fn child() {
+ let mut stdin = io::stdin();
+ for line in stdin.lock().lines() {
+ println!("{}", line.unwrap());
+ }
+}
diff --git a/src/test/ui/process/issue-14456.rs b/src/test/ui/process/issue-14456.rs
new file mode 100644
index 000000000..52a56eb77
--- /dev/null
+++ b/src/test/ui/process/issue-14456.rs
@@ -0,0 +1,37 @@
+// run-pass
+#![allow(unused_mut)]
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::env;
+use std::io::prelude::*;
+use std::io;
+use std::process::{Command, Stdio};
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ if args.len() > 1 && args[1] == "child" {
+ return child()
+ }
+
+ test();
+}
+
+fn child() {
+ writeln!(&mut io::stdout(), "foo").unwrap();
+ writeln!(&mut io::stderr(), "bar").unwrap();
+ let mut stdin = io::stdin();
+ let mut s = String::new();
+ stdin.lock().read_line(&mut s).unwrap();
+ assert_eq!(s.len(), 0);
+}
+
+fn test() {
+ let args: Vec<String> = env::args().collect();
+ let mut p = Command::new(&args[0]).arg("child")
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn().unwrap();
+ assert!(p.wait().unwrap().success());
+}
diff --git a/src/test/ui/process/issue-14940.rs b/src/test/ui/process/issue-14940.rs
new file mode 100644
index 000000000..98a4af0c4
--- /dev/null
+++ b/src/test/ui/process/issue-14940.rs
@@ -0,0 +1,19 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::env;
+use std::process::Command;
+use std::io::{self, Write};
+
+fn main() {
+ let mut args = env::args();
+ if args.len() > 1 {
+ let mut out = io::stdout();
+ out.write(&['a' as u8; 128 * 1024]).unwrap();
+ } else {
+ let out = Command::new(&args.next().unwrap()).arg("child").output();
+ let out = out.unwrap();
+ assert!(out.status.success());
+ }
+}
diff --git a/src/test/ui/process/issue-16272.rs b/src/test/ui/process/issue-16272.rs
new file mode 100644
index 000000000..5cf3fd949
--- /dev/null
+++ b/src/test/ui/process/issue-16272.rs
@@ -0,0 +1,23 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::process::Command;
+use std::env;
+
+fn main() {
+ let len = env::args().len();
+
+ if len == 1 {
+ test();
+ } else {
+ assert_eq!(len, 3);
+ }
+}
+
+fn test() {
+ let status = Command::new(&env::current_exe().unwrap())
+ .arg("foo").arg("")
+ .status().unwrap();
+ assert!(status.success());
+}
diff --git a/src/test/ui/process/issue-20091.rs b/src/test/ui/process/issue-20091.rs
new file mode 100644
index 000000000..86cc79d6b
--- /dev/null
+++ b/src/test/ui/process/issue-20091.rs
@@ -0,0 +1,24 @@
+// run-pass
+#![allow(stable_features)]
+
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+#![feature(os)]
+
+#[cfg(unix)]
+fn main() {
+ use std::process::Command;
+ use std::env;
+ use std::os::unix::prelude::*;
+ use std::ffi::OsStr;
+
+ if env::args().len() == 1 {
+ assert!(Command::new(&env::current_exe().unwrap())
+ .arg(<OsStr as OsStrExt>::from_bytes(b"\xff"))
+ .status().unwrap().success())
+ }
+}
+
+#[cfg(windows)]
+fn main() {}
diff --git a/src/test/ui/process/multi-panic.rs b/src/test/ui/process/multi-panic.rs
new file mode 100644
index 000000000..a1887c218
--- /dev/null
+++ b/src/test/ui/process/multi-panic.rs
@@ -0,0 +1,38 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// needs-unwind
+
+fn check_for_no_backtrace(test: std::process::Output) {
+ assert!(!test.status.success());
+ let err = String::from_utf8_lossy(&test.stderr);
+ let mut it = err.lines();
+
+ assert_eq!(it.next().map(|l| l.starts_with("thread '<unnamed>' panicked at")), Some(true));
+ assert_eq!(it.next(), Some("note: run with `RUST_BACKTRACE=1` \
+ environment variable to display a backtrace"));
+ assert_eq!(it.next().map(|l| l.starts_with("thread 'main' panicked at")), Some(true));
+ assert_eq!(it.next(), None);
+}
+
+fn main() {
+ let args: Vec<String> = std::env::args().collect();
+ if args.len() > 1 && args[1] == "run_test" {
+ let _ = std::thread::spawn(|| {
+ panic!();
+ }).join();
+
+ panic!();
+ } else {
+ let test = std::process::Command::new(&args[0]).arg("run_test")
+ .env_remove("RUST_BACKTRACE")
+ .output()
+ .unwrap();
+ check_for_no_backtrace(test);
+ let test = std::process::Command::new(&args[0]).arg("run_test")
+ .env("RUST_BACKTRACE","0")
+ .output()
+ .unwrap();
+ check_for_no_backtrace(test);
+ }
+}
diff --git a/src/test/ui/process/no-stdio.rs b/src/test/ui/process/no-stdio.rs
new file mode 100644
index 000000000..24985386a
--- /dev/null
+++ b/src/test/ui/process/no-stdio.rs
@@ -0,0 +1,139 @@
+// run-pass
+// ignore-android
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// revisions: mir thir
+// [thir]compile-flags: -Zthir-unsafeck
+
+#![feature(rustc_private)]
+
+extern crate libc;
+
+use std::process::{Command, Stdio};
+use std::env;
+use std::io::{self, Read, Write};
+
+#[cfg(unix)]
+unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
+ let doit = |a| {
+ let r = libc::dup(a);
+ assert!(r >= 0);
+ return r
+ };
+ let a = doit(0);
+ let b = doit(1);
+ let c = doit(2);
+
+ assert!(libc::close(0) >= 0);
+ assert!(libc::close(1) >= 0);
+ assert!(libc::close(2) >= 0);
+
+ let r = f();
+
+ assert!(libc::dup2(a, 0) >= 0);
+ assert!(libc::dup2(b, 1) >= 0);
+ assert!(libc::dup2(c, 2) >= 0);
+
+ return r
+}
+
+#[cfg(unix)]
+fn assert_fd_is_valid(fd: libc::c_int) {
+ if unsafe { libc::fcntl(fd, libc::F_GETFD) == -1 } {
+ panic!("file descriptor {} is not valid: {}", fd, io::Error::last_os_error());
+ }
+}
+
+#[cfg(windows)]
+fn assert_fd_is_valid(_fd: libc::c_int) {}
+
+#[cfg(windows)]
+unsafe fn without_stdio<R, F: FnOnce() -> R>(f: F) -> R {
+ type DWORD = u32;
+ type HANDLE = *mut u8;
+ type BOOL = i32;
+
+ const STD_INPUT_HANDLE: DWORD = -10i32 as DWORD;
+ const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
+ const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD;
+ const INVALID_HANDLE_VALUE: HANDLE = !0 as HANDLE;
+
+ extern "system" {
+ fn GetStdHandle(which: DWORD) -> HANDLE;
+ fn SetStdHandle(which: DWORD, handle: HANDLE) -> BOOL;
+ }
+
+ let doit = |id| {
+ let handle = GetStdHandle(id);
+ assert!(handle != INVALID_HANDLE_VALUE);
+ assert!(SetStdHandle(id, INVALID_HANDLE_VALUE) != 0);
+ return handle
+ };
+
+ let a = doit(STD_INPUT_HANDLE);
+ let b = doit(STD_OUTPUT_HANDLE);
+ let c = doit(STD_ERROR_HANDLE);
+
+ let r = f();
+
+ let doit = |id, handle| {
+ assert!(SetStdHandle(id, handle) != 0);
+ };
+ doit(STD_INPUT_HANDLE, a);
+ doit(STD_OUTPUT_HANDLE, b);
+ doit(STD_ERROR_HANDLE, c);
+
+ return r
+}
+
+fn main() {
+ if env::args().len() > 1 {
+ // Writing to stdout & stderr should not panic.
+ println!("test");
+ assert!(io::stdout().write(b"test\n").is_ok());
+ assert!(io::stderr().write(b"test\n").is_ok());
+
+ // Stdin should be at EOF.
+ assert_eq!(io::stdin().read(&mut [0; 10]).unwrap(), 0);
+
+ // Standard file descriptors should be valid on UNIX:
+ assert_fd_is_valid(0);
+ assert_fd_is_valid(1);
+ assert_fd_is_valid(2);
+ return
+ }
+
+ // First, make sure reads/writes without stdio work if stdio itself is
+ // missing.
+ let (a, b, c) = unsafe {
+ without_stdio(|| {
+ let a = io::stdout().write(b"test\n");
+ let b = io::stderr().write(b"test\n");
+ let c = io::stdin().read(&mut [0; 10]);
+
+ (a, b, c)
+ })
+ };
+
+ assert_eq!(a.unwrap(), 5);
+ assert_eq!(b.unwrap(), 5);
+ assert_eq!(c.unwrap(), 0);
+
+ // Second, spawn a child and do some work with "null" descriptors to make
+ // sure it's ok
+ let me = env::current_exe().unwrap();
+ let status = Command::new(&me)
+ .arg("next")
+ .stdin(Stdio::null())
+ .stdout(Stdio::null())
+ .stderr(Stdio::null())
+ .status().unwrap();
+ assert!(status.success(), "{} isn't a success", status);
+
+ // Finally, close everything then spawn a child to make sure everything is
+ // *still* ok.
+ let status = unsafe {
+ without_stdio(|| Command::new(&me).arg("next").status())
+ }.unwrap();
+ assert!(status.success(), "{} isn't a success", status);
+}
diff --git a/src/test/ui/process/nofile-limit.rs b/src/test/ui/process/nofile-limit.rs
new file mode 100644
index 000000000..3ddf8d6ef
--- /dev/null
+++ b/src/test/ui/process/nofile-limit.rs
@@ -0,0 +1,46 @@
+// Check that statically linked binary executes successfully
+// with RLIMIT_NOFILE resource lowered to zero. Regression
+// test for issue #96621.
+//
+// run-pass
+// dont-check-compiler-stderr
+// only-linux
+// no-prefer-dynamic
+// compile-flags: -Ctarget-feature=+crt-static -Crpath=no -Crelocation-model=static
+#![feature(exit_status_error)]
+#![feature(rustc_private)]
+extern crate libc;
+
+use std::os::unix::process::CommandExt;
+use std::process::Command;
+
+fn main() {
+ let mut args = std::env::args();
+ let this = args.next().unwrap();
+ match args.next().as_deref() {
+ None => {
+ let mut cmd = Command::new(this);
+ cmd.arg("Ok!");
+ unsafe {
+ cmd.pre_exec(|| {
+ let rlim = libc::rlimit {
+ rlim_cur: 0,
+ rlim_max: 0,
+ };
+ if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) == -1 {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(())
+ }
+ })
+ };
+ let output = cmd.output().unwrap();
+ println!("{:?}", output);
+ output.status.exit_ok().unwrap();
+ assert!(output.stdout.starts_with(b"Ok!"));
+ }
+ Some(word) => {
+ println!("{}", word);
+ }
+ }
+}
diff --git a/src/test/ui/process/process-envs.rs b/src/test/ui/process/process-envs.rs
new file mode 100644
index 000000000..8fc99b23f
--- /dev/null
+++ b/src/test/ui/process/process-envs.rs
@@ -0,0 +1,53 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-vxworks no 'env'
+
+use std::process::Command;
+use std::env;
+use std::collections::HashMap;
+
+#[cfg(all(unix, not(target_os="android")))]
+pub fn env_cmd() -> Command {
+ Command::new("env")
+}
+#[cfg(target_os="android")]
+pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("/system/bin/sh");
+ cmd.arg("-c").arg("set");
+ cmd
+}
+
+#[cfg(windows)]
+pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("cmd");
+ cmd.arg("/c").arg("set");
+ cmd
+}
+
+fn main() {
+ // save original environment
+ let old_env = env::var_os("RUN_TEST_NEW_ENV");
+
+ env::set_var("RUN_TEST_NEW_ENV", "123");
+
+ // create filtered environment vector
+ let filtered_env : HashMap<String, String> =
+ env::vars().filter(|&(ref k, _)| k == "PATH").collect();
+
+ let mut cmd = env_cmd();
+ cmd.env_clear();
+ cmd.envs(&filtered_env);
+
+ // restore original environment
+ match old_env {
+ None => env::remove_var("RUN_TEST_NEW_ENV"),
+ Some(val) => env::set_var("RUN_TEST_NEW_ENV", &val)
+ }
+
+ let result = cmd.output().unwrap();
+ let output = String::from_utf8_lossy(&result.stdout);
+
+ assert!(!output.contains("RUN_TEST_NEW_ENV"),
+ "found RUN_TEST_NEW_ENV inside of:\n\n{}", output);
+}
diff --git a/src/test/ui/process/process-exit.rs b/src/test/ui/process/process-exit.rs
new file mode 100644
index 000000000..d193e073e
--- /dev/null
+++ b/src/test/ui/process/process-exit.rs
@@ -0,0 +1,26 @@
+// run-pass
+#![allow(unused_imports)]
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::env;
+use std::process::{self, Command, Stdio};
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ if args.len() > 1 && args[1] == "child" {
+ child();
+ } else {
+ parent();
+ }
+}
+
+fn parent() {
+ let args: Vec<String> = env::args().collect();
+ let status = Command::new(&args[0]).arg("child").status().unwrap();
+ assert_eq!(status.code(), Some(2));
+}
+
+fn child() -> i32 {
+ process::exit(2);
+}
diff --git a/src/test/ui/process/process-panic-after-fork.rs b/src/test/ui/process/process-panic-after-fork.rs
new file mode 100644
index 000000000..1ccf6bb05
--- /dev/null
+++ b/src/test/ui/process/process-panic-after-fork.rs
@@ -0,0 +1,151 @@
+// run-pass
+// no-prefer-dynamic
+// ignore-wasm32-bare no libc
+// ignore-windows
+// ignore-sgx no libc
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-android: FIXME(#85261)
+
+#![feature(bench_black_box)]
+#![feature(rustc_private)]
+#![feature(never_type)]
+#![feature(panic_always_abort)]
+
+extern crate libc;
+
+use std::alloc::{GlobalAlloc, Layout};
+use std::fmt;
+use std::panic::{self, panic_any};
+use std::os::unix::process::{CommandExt, ExitStatusExt};
+use std::process::{self, Command, ExitStatus};
+use std::sync::atomic::{AtomicU32, Ordering};
+
+use libc::c_int;
+
+/// This stunt allocator allows us to spot heap allocations in the child.
+struct PidChecking<A> {
+ parent: A,
+ require_pid: AtomicU32,
+}
+
+#[global_allocator]
+static ALLOCATOR: PidChecking<std::alloc::System> = PidChecking {
+ parent: std::alloc::System,
+ require_pid: AtomicU32::new(0),
+};
+
+impl<A> PidChecking<A> {
+ fn engage(&self) {
+ let parent_pid = process::id();
+ eprintln!("engaging allocator trap, parent pid={}", parent_pid);
+ self.require_pid.store(parent_pid, Ordering::Release);
+ }
+ fn check(&self) {
+ let require_pid = self.require_pid.load(Ordering::Acquire);
+ if require_pid != 0 {
+ let actual_pid = process::id();
+ if require_pid != actual_pid {
+ unsafe {
+ libc::raise(libc::SIGUSR1);
+ }
+ }
+ }
+ }
+}
+
+unsafe impl<A:GlobalAlloc> GlobalAlloc for PidChecking<A> {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ self.check();
+ self.parent.alloc(layout)
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ self.check();
+ self.parent.dealloc(ptr, layout)
+ }
+
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ self.check();
+ self.parent.alloc_zeroed(layout)
+ }
+
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ self.check();
+ self.parent.realloc(ptr, layout, new_size)
+ }
+}
+
+fn expect_aborted(status: ExitStatus) {
+ dbg!(status);
+ let signal = status.signal().expect("expected child process to die of signal");
+ assert!(signal == libc::SIGABRT || signal == libc::SIGILL || signal == libc::SIGTRAP);
+}
+
+fn main() {
+ ALLOCATOR.engage();
+
+ fn run(do_panic: &dyn Fn()) -> ExitStatus {
+ let child = unsafe { libc::fork() };
+ assert!(child >= 0);
+ if child == 0 {
+ panic::always_abort();
+ do_panic();
+ process::exit(0);
+ }
+ let mut status: c_int = 0;
+ let got = unsafe { libc::waitpid(child, &mut status, 0) };
+ assert_eq!(got, child);
+ let status = ExitStatus::from_raw(status.into());
+ status
+ }
+
+ fn one(do_panic: &dyn Fn()) {
+ let status = run(do_panic);
+ expect_aborted(status);
+ }
+
+ one(&|| panic!());
+ one(&|| panic!("some message"));
+ one(&|| panic!("message with argument: {}", 42));
+
+ #[derive(Debug)]
+ struct Wotsit { }
+ one(&|| panic_any(Wotsit { }));
+
+ let mut c = Command::new("echo");
+ unsafe {
+ c.pre_exec(|| panic!("{}", "crash now!"));
+ }
+ let st = c.status().expect("failed to get command status");
+ expect_aborted(st);
+
+ struct DisplayWithHeap;
+ impl fmt::Display for DisplayWithHeap {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ let s = vec![0; 100];
+ let s = std::hint::black_box(s);
+ write!(f, "{:?}", s)
+ }
+ }
+
+ // Some panics in the stdlib that we want not to allocate, as
+ // otherwise these facilities become impossible to use in the
+ // child after fork, which is really quite awkward.
+
+ one(&|| { None::<DisplayWithHeap>.unwrap(); });
+ one(&|| { None::<DisplayWithHeap>.expect("unwrapped a none"); });
+ one(&|| { std::str::from_utf8(b"\xff").unwrap(); });
+ one(&|| {
+ let x = [0, 1, 2, 3];
+ let y = x[std::hint::black_box(4)];
+ let _z = std::hint::black_box(y);
+ });
+
+ // Finally, check that our stunt allocator can actually catch an allocation after fork.
+ // ie, that our test is effective.
+
+ let status = run(&|| panic!("allocating to display... {}", DisplayWithHeap));
+ dbg!(status);
+ assert_eq!(status.signal(), Some(libc::SIGUSR1));
+}
diff --git a/src/test/ui/process/process-remove-from-env.rs b/src/test/ui/process/process-remove-from-env.rs
new file mode 100644
index 000000000..af4e49dfd
--- /dev/null
+++ b/src/test/ui/process/process-remove-from-env.rs
@@ -0,0 +1,47 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-vxworks no 'env'
+
+use std::process::Command;
+use std::env;
+
+#[cfg(all(unix, not(target_os="android")))]
+pub fn env_cmd() -> Command {
+ Command::new("env")
+}
+#[cfg(target_os="android")]
+pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("/system/bin/sh");
+ cmd.arg("-c").arg("set");
+ cmd
+}
+
+#[cfg(windows)]
+pub fn env_cmd() -> Command {
+ let mut cmd = Command::new("cmd");
+ cmd.arg("/c").arg("set");
+ cmd
+}
+
+fn main() {
+ // save original environment
+ let old_env = env::var_os("RUN_TEST_NEW_ENV");
+
+ env::set_var("RUN_TEST_NEW_ENV", "123");
+
+ let mut cmd = env_cmd();
+ cmd.env_remove("RUN_TEST_NEW_ENV");
+
+ // restore original environment
+ match old_env {
+ None => env::remove_var("RUN_TEST_NEW_ENV"),
+ Some(val) => env::set_var("RUN_TEST_NEW_ENV", &val)
+ }
+
+ let result = cmd.output().unwrap();
+ let output = String::from_utf8_lossy(&result.stdout);
+
+ assert!(!output.contains("RUN_TEST_NEW_ENV"),
+ "found RUN_TEST_NEW_ENV inside of:\n\n{}", output);
+}
diff --git a/src/test/ui/process/process-sigpipe.rs b/src/test/ui/process/process-sigpipe.rs
new file mode 100644
index 000000000..ecf5e93c9
--- /dev/null
+++ b/src/test/ui/process/process-sigpipe.rs
@@ -0,0 +1,36 @@
+// run-pass
+#![allow(unused_imports)]
+#![allow(deprecated)]
+
+// ignore-android since the dynamic linker sets a SIGPIPE handler (to do
+// a crash report) so inheritance is moot on the entire platform
+
+// libstd ignores SIGPIPE, and other libraries may set signal masks.
+// Make sure that these behaviors don't get inherited to children
+// spawned via std::process, since they're needed for traditional UNIX
+// filter behavior. This test checks that `yes | head` terminates
+// (instead of running forever), and that it does not print an error
+// message about a broken pipe.
+
+// ignore-emscripten no threads support
+// ignore-vxworks no 'sh'
+
+use std::process;
+use std::thread;
+
+#[cfg(unix)]
+fn main() {
+ // Just in case `yes` doesn't check for EPIPE...
+ thread::spawn(|| {
+ thread::sleep_ms(5000);
+ process::exit(1);
+ });
+ let output = process::Command::new("sh").arg("-c").arg("yes | head").output().unwrap();
+ assert!(output.status.success());
+ assert!(output.stderr.len() == 0);
+}
+
+#[cfg(not(unix))]
+fn main() {
+ // Not worried about signal masks on other platforms
+}
diff --git a/src/test/ui/process/process-spawn-nonexistent.rs b/src/test/ui/process/process-spawn-nonexistent.rs
new file mode 100644
index 000000000..a51372263
--- /dev/null
+++ b/src/test/ui/process/process-spawn-nonexistent.rs
@@ -0,0 +1,16 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::io::ErrorKind;
+use std::process::Command;
+
+fn main() {
+ let result = Command::new("nonexistent").spawn().unwrap_err().kind();
+
+ assert!(matches!(
+ result,
+ // Under WSL with appendWindowsPath=true, this fails with PermissionDenied
+ ErrorKind::NotFound | ErrorKind::PermissionDenied
+ ));
+}
diff --git a/src/test/ui/process/process-spawn-with-unicode-params.rs b/src/test/ui/process/process-spawn-with-unicode-params.rs
new file mode 100644
index 000000000..6e9229b62
--- /dev/null
+++ b/src/test/ui/process/process-spawn-with-unicode-params.rs
@@ -0,0 +1,76 @@
+// run-pass
+// no-prefer-dynamic
+
+// The test copies itself into a subdirectory with a non-ASCII name and then
+// runs it as a child process within the subdirectory. The parent process
+// also adds an environment variable and an argument, both containing
+// non-ASCII characters. The child process ensures all the strings are
+// intact.
+
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::io::prelude::*;
+use std::io;
+use std::fs;
+use std::process::Command;
+use std::env;
+use std::path::Path;
+
+fn main() {
+ let my_args = env::args().collect::<Vec<_>>();
+ let my_cwd = env::current_dir().unwrap();
+ let my_env = env::vars().collect::<Vec<_>>();
+ let my_path = env::current_exe().unwrap();
+ let my_dir = my_path.parent().unwrap();
+ let my_ext = my_path.extension().and_then(|s| s.to_str()).unwrap_or("");
+
+ // some non-ASCII characters
+ let blah = "\u{3c0}\u{42f}\u{97f3}\u{e6}\u{221e}";
+
+ let child_name = "child";
+ let child_dir = format!("process-spawn-with-unicode-params-{}", blah);
+
+ // parameters sent to child / expected to be received from parent
+ let arg = blah;
+ let cwd = my_dir.join(&child_dir);
+ let env = ("RUST_TEST_PROC_SPAWN_UNICODE".to_string(), blah.to_string());
+
+ // am I the parent or the child?
+ if my_args.len() == 1 { // parent
+
+ let child_filestem = Path::new(child_name);
+ let child_filename = child_filestem.with_extension(my_ext);
+ let child_path = cwd.join(&child_filename);
+
+ // make a separate directory for the child
+ let _ = fs::create_dir(&cwd);
+ fs::copy(&my_path, &child_path).unwrap();
+
+ // run child
+ let p = Command::new(&child_path)
+ .arg(arg)
+ .current_dir(&cwd)
+ .env(&env.0, &env.1)
+ .spawn().unwrap().wait_with_output().unwrap();
+
+ // display the output
+ io::stdout().write_all(&p.stdout).unwrap();
+ io::stderr().write_all(&p.stderr).unwrap();
+
+ // make sure the child succeeded
+ assert!(p.status.success());
+
+ } else { // child
+
+ // check working directory (don't try to compare with `cwd` here!)
+ assert!(my_cwd.ends_with(&child_dir));
+
+ // check arguments
+ assert_eq!(&*my_args[1], arg);
+
+ // check environment variable
+ assert!(my_env.contains(&env));
+
+ };
+}
diff --git a/src/test/ui/process/process-status-inherits-stdin.rs b/src/test/ui/process/process-status-inherits-stdin.rs
new file mode 100644
index 000000000..7719dd9ad
--- /dev/null
+++ b/src/test/ui/process/process-status-inherits-stdin.rs
@@ -0,0 +1,35 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::env;
+use std::io;
+use std::io::Write;
+use std::process::{Command, Stdio};
+
+fn main() {
+ let mut args = env::args();
+ let me = args.next().unwrap();
+ let arg = args.next();
+ match arg.as_ref().map(|s| &s[..]) {
+ None => {
+ let mut s = Command::new(&me)
+ .arg("a1")
+ .stdin(Stdio::piped())
+ .spawn()
+ .unwrap();
+ s.stdin.take().unwrap().write_all(b"foo\n").unwrap();
+ let s = s.wait().unwrap();
+ assert!(s.success());
+ }
+ Some("a1") => {
+ let s = Command::new(&me).arg("a2").status().unwrap();
+ assert!(s.success());
+ }
+ Some(..) => {
+ let mut s = String::new();
+ io::stdin().read_line(&mut s).unwrap();
+ assert_eq!(s, "foo\n");
+ }
+ }
+}
diff --git a/src/test/ui/process/signal-exit-status.rs b/src/test/ui/process/signal-exit-status.rs
new file mode 100644
index 000000000..0963dcc80
--- /dev/null
+++ b/src/test/ui/process/signal-exit-status.rs
@@ -0,0 +1,18 @@
+// run-pass
+// ignore-emscripten no processes
+// ignore-sgx no processes
+// ignore-windows
+
+use std::env;
+use std::process::Command;
+
+pub fn main() {
+ let args: Vec<String> = env::args().collect();
+ if args.len() >= 2 && args[1] == "signal" {
+ // Raise a segfault.
+ unsafe { *(1 as *mut isize) = 0; }
+ } else {
+ let status = Command::new(&args[0]).arg("signal").status().unwrap();
+ assert!(status.code().is_none());
+ }
+}
diff --git a/src/test/ui/process/sigpipe-should-be-ignored.rs b/src/test/ui/process/sigpipe-should-be-ignored.rs
new file mode 100644
index 000000000..144eeca23
--- /dev/null
+++ b/src/test/ui/process/sigpipe-should-be-ignored.rs
@@ -0,0 +1,33 @@
+// run-pass
+
+#![allow(unused_must_use)]
+// Be sure that when a SIGPIPE would have been received that the entire process
+// doesn't die in a ball of fire, but rather it's gracefully handled.
+
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+use std::env;
+use std::io::prelude::*;
+use std::io;
+use std::process::{Command, Stdio};
+
+fn test() {
+ let _ = io::stdin().read_line(&mut String::new());
+ io::stdout().write(&[1]);
+ assert!(io::stdout().flush().is_err());
+}
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ if args.len() > 1 && args[1] == "test" {
+ return test();
+ }
+
+ let mut p = Command::new(&args[0])
+ .stdout(Stdio::piped())
+ .stdin(Stdio::piped())
+ .arg("test").spawn().unwrap();
+ drop(p.stdout.take());
+ assert!(p.wait().unwrap().success());
+}
diff --git a/src/test/ui/process/tls-exit-status.rs b/src/test/ui/process/tls-exit-status.rs
new file mode 100644
index 000000000..6296e5042
--- /dev/null
+++ b/src/test/ui/process/tls-exit-status.rs
@@ -0,0 +1,11 @@
+// run-fail
+// error-pattern:nonzero
+// exec-env:RUST_NEWRT=1
+// ignore-emscripten no processes
+
+use std::env;
+
+fn main() {
+ env::args();
+ panic!("please have a nonzero exit status");
+}
diff --git a/src/test/ui/process/try-wait.rs b/src/test/ui/process/try-wait.rs
new file mode 100644
index 000000000..692197210
--- /dev/null
+++ b/src/test/ui/process/try-wait.rs
@@ -0,0 +1,60 @@
+// run-pass
+
+#![allow(stable_features)]
+// ignore-emscripten no processes
+// ignore-sgx no processes
+
+#![feature(process_try_wait)]
+
+use std::env;
+use std::process::Command;
+use std::thread;
+use std::time::Duration;
+
+fn main() {
+ let args = env::args().collect::<Vec<_>>();
+ if args.len() != 1 {
+ match &args[1][..] {
+ "sleep" => thread::sleep(Duration::new(1_000, 0)),
+ _ => {}
+ }
+ return
+ }
+
+ let mut me = Command::new(env::current_exe().unwrap())
+ .arg("sleep")
+ .spawn()
+ .unwrap();
+ let maybe_status = me.try_wait().unwrap();
+ assert!(maybe_status.is_none());
+ let maybe_status = me.try_wait().unwrap();
+ assert!(maybe_status.is_none());
+
+ me.kill().unwrap();
+ me.wait().unwrap();
+
+ let status = me.try_wait().unwrap().unwrap();
+ assert!(!status.success());
+ let status = me.try_wait().unwrap().unwrap();
+ assert!(!status.success());
+
+ let mut me = Command::new(env::current_exe().unwrap())
+ .arg("return-quickly")
+ .spawn()
+ .unwrap();
+ loop {
+ match me.try_wait() {
+ Ok(Some(res)) => {
+ assert!(res.success());
+ break
+ }
+ Ok(None) => {
+ thread::sleep(Duration::from_millis(1));
+ }
+ Err(e) => panic!("error in try_wait: {}", e),
+ }
+ }
+
+ let status = me.try_wait().unwrap().unwrap();
+ assert!(status.success());
+}