Skip to content

Commit e0a53ed

Browse files
committed
Use fcntl(fd, F_GETFD) to detect if standard streams are open
In the previous implementation, if the standard streams were open, but the RLIMIT_NOFILE value was below three, the poll would fail with EINVAL: > ERRORS: EINVAL The nfds value exceeds the RLIMIT_NOFILE value. Switch to the existing fcntl based implementation to avoid the issue.
1 parent b862b43 commit e0a53ed

File tree

2 files changed

+52
-31
lines changed

2 files changed

+52
-31
lines changed

library/std/src/sys/unix/mod.rs

+6-31
Original file line numberDiff line numberDiff line change
@@ -67,48 +67,23 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
6767
args::init(argc, argv);
6868

6969
unsafe fn sanitize_standard_fds() {
70-
#[cfg(not(miri))]
71-
// The standard fds are always available in Miri.
7270
cfg_if::cfg_if! {
7371
if #[cfg(not(any(
72+
// The standard fds are always available in Miri.
73+
miri,
7474
target_os = "emscripten",
7575
target_os = "fuchsia",
7676
target_os = "vxworks",
77-
// The poll on Darwin doesn't set POLLNVAL for closed fds.
78-
target_os = "macos",
79-
target_os = "ios",
80-
target_os = "redox",
8177
target_os = "l4re",
8278
)))] {
83-
use crate::sys::os::errno;
84-
let pfds: &mut [_] = &mut [
85-
libc::pollfd { fd: 0, events: 0, revents: 0 },
86-
libc::pollfd { fd: 1, events: 0, revents: 0 },
87-
libc::pollfd { fd: 2, events: 0, revents: 0 },
88-
];
89-
while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 {
90-
if errno() == libc::EINTR {
91-
continue;
92-
}
93-
libc::abort();
94-
}
95-
for pfd in pfds {
96-
if pfd.revents & libc::POLLNVAL == 0 {
97-
continue;
98-
}
99-
if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
100-
// If the stream is closed but we failed to reopen it, abort the
101-
// process. Otherwise we wouldn't preserve the safety of
102-
// operations on the corresponding Rust object Stdin, Stdout, or
103-
// Stderr.
104-
libc::abort();
105-
}
106-
}
107-
} else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] {
10879
use crate::sys::os::errno;
10980
for fd in 0..3 {
11081
if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
11182
if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
83+
// If the stream is closed but we failed to reopen it, abort the
84+
// process. Otherwise we wouldn't preserve the safety of
85+
// operations on the corresponding Rust object Stdin, Stdout, or
86+
// Stderr.
11287
libc::abort();
11388
}
11489
}

src/test/ui/process/nofile-limit.rs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Check that statically linked binary executes successfully
2+
// with RLIMIT_NOFILE resource lowered to zero. Regression
3+
// test for issue #96621.
4+
//
5+
// run-pass
6+
// dont-check-compiler-stderr
7+
// only-linux
8+
// no-prefer-dynamic
9+
// compile-flags: -Ctarget-feature=+crt-static -Crpath=no
10+
#![feature(exit_status_error)]
11+
#![feature(rustc_private)]
12+
extern crate libc;
13+
14+
use std::os::unix::process::CommandExt;
15+
use std::process::Command;
16+
17+
fn main() {
18+
let mut args = std::env::args();
19+
let this = args.next().unwrap();
20+
match args.next().as_deref() {
21+
None => {
22+
let mut cmd = Command::new(this);
23+
cmd.arg("Ok!");
24+
unsafe {
25+
cmd.pre_exec(|| {
26+
let rlim = libc::rlimit {
27+
rlim_cur: 0,
28+
rlim_max: 0,
29+
};
30+
if libc::setrlimit(libc::RLIMIT_NOFILE, &rlim) == -1 {
31+
Err(std::io::Error::last_os_error())
32+
} else {
33+
Ok(())
34+
}
35+
})
36+
};
37+
let output = cmd.output().unwrap();
38+
println!("{:?}", output);
39+
output.status.exit_ok().unwrap();
40+
assert!(output.stdout.starts_with(b"Ok!"));
41+
}
42+
Some(word) => {
43+
println!("{}", word);
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)