Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
73ada2d
Explicitly document the size guarantees that Option makes.
ltratt Aug 12, 2020
f5118a5
Clarify and add guarantee about `transmute`.
ltratt Aug 12, 2020
83f47aa
Be clear about the reverse `transmute` guarantees.
ltratt Aug 12, 2020
f3d7196
Be clearer about Some/None transmute.
ltratt Aug 12, 2020
8cb8955
Change notation.
ltratt Aug 12, 2020
55802e3
Add Rust function pointers.
ltratt Aug 12, 2020
68209c3
Rename the types for clarity.
ltratt Aug 17, 2020
9bac577
Grammar tweak.
ltratt Aug 17, 2020
2ac89ff
Point at named argument not found when using `format_args_capture` in…
estebank Sep 8, 2020
094d67a
Add accessors to Command.
ehuss Sep 21, 2020
945a732
Update mdBook
camelid Sep 23, 2020
50d9663
Update cargo
ehuss Sep 24, 2020
9baa601
Add `x.py setup`
jyn514 Sep 12, 2020
bab15f7
Remove std::io::lazy::Lazy in favour of SyncOnceCell
m-ou-se Sep 24, 2020
e9b25f5
Add test to check stdout flushing during shutdown.
m-ou-se Sep 24, 2020
45700a9
Drop use of Arc from Stdin and Stdout.
m-ou-se Sep 24, 2020
12ada5c
Remove TrustedLen requirement from BuilderMethods::switch
est31 Sep 24, 2020
6f9c132
Call ReentrantMutex::init() in stdout().
m-ou-se Sep 24, 2020
47843f5
update Miri
RalfJung Sep 24, 2020
257f6e5
Reopen standard streams when they are closed on Unix
tmiasko Sep 25, 2020
187162e
Add missing code examples on slice iter types
GuillaumeGomez Sep 22, 2020
900daba
Remove stray word from `ClosureKind::extends` docs
LingMan Sep 25, 2020
aa6a2f4
Rename `whence` to `span`
camelid Sep 25, 2020
41e8c8c
Rollup merge of #75295 - tmiasko:fds, r=Amanieu
jonas-schievink Sep 26, 2020
d00ca17
Rollup merge of #75454 - ltratt:option_optimisation_guarantees, r=dto…
jonas-schievink Sep 26, 2020
f0caf1d
Rollup merge of #76485 - estebank:format_arg_capture_spans, r=davidtwco
jonas-schievink Sep 26, 2020
73c3a67
Rollup merge of #76631 - jyn514:x.py-setup, r=Mark-Simulacrum
jonas-schievink Sep 26, 2020
df98bae
Rollup merge of #77029 - ehuss:command-access, r=Mark-Simulacrum
jonas-schievink Sep 26, 2020
1f38dba
Rollup merge of #77076 - GuillaumeGomez:missing-code-examples-slice-i…
jonas-schievink Sep 26, 2020
81945dc
Rollup merge of #77127 - camelid:update-mdbook, r=Dylan-DPC
jonas-schievink Sep 26, 2020
d421d4e
Rollup merge of #77129 - ehuss:update-cargo, r=ehuss
jonas-schievink Sep 26, 2020
0206cfc
Rollup merge of #77154 - fusion-engineering-forks:lazy-stdio, r=dtolnay
jonas-schievink Sep 26, 2020
216ed7b
Rollup merge of #77161 - est31:swich_len_already_trusted, r=petrochenkov
jonas-schievink Sep 26, 2020
7776d68
Rollup merge of #77166 - RalfJung:miri, r=RalfJung
jonas-schievink Sep 26, 2020
7e160b6
Rollup merge of #77204 - LingMan:patch-3, r=jonas-schievink
jonas-schievink Sep 26, 2020
bfb0d2d
Rollup merge of #77207 - camelid:whence-to-span, r=jyn514
jonas-schievink Sep 26, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Reopen standard streams when they are closed on Unix
The syscalls returning a new file descriptors generally use
lowest-numbered file descriptor not currently opened, without any
exceptions for those corresponding to the standard streams.

Previously when any of standard streams has been closed before starting
the application, operations on std::io::{stderr,stdin,stdout} objects
were likely to operate on other logically unrelated file resources
opened afterwards.

Avoid the issue by reopening the standard streams when they are closed.
  • Loading branch information
tmiasko committed Sep 25, 2020
commit 257f6e5f745c25b66e432f5222d1b746469fcdb0
62 changes: 62 additions & 0 deletions library/std/src/sys/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ pub use crate::sys_common::os_str_bytes as os_str;

#[cfg(not(test))]
pub fn init() {
// The standard streams might be closed on application startup. To prevent
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
// resources opened later, we reopen standards streams when they are closed.
unsafe {
sanitize_standard_fds();
}

// By default, some platforms will send a *signal* when an EPIPE error
// would otherwise be delivered. This runtime doesn't install a SIGPIPE
// handler, causing it to kill the program, which isn't exactly what we
Expand All @@ -86,6 +93,61 @@ pub fn init() {
reset_sigpipe();
}

// In the case when all file descriptors are open, the poll has been
// observed to perform better than fcntl (on GNU/Linux).
#[cfg(not(any(
miri,
target_os = "emscripten",
target_os = "fuchsia",
// The poll on Darwin doesn't set POLLNVAL for closed fds.
target_os = "macos",
target_os = "ios",
target_os = "redox",
)))]
unsafe fn sanitize_standard_fds() {
use crate::sys::os::errno;
let pfds: &mut [_] = &mut [
libc::pollfd { fd: 0, events: 0, revents: 0 },
libc::pollfd { fd: 1, events: 0, revents: 0 },
libc::pollfd { fd: 2, events: 0, revents: 0 },
];
while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 {
if errno() == libc::EINTR {
continue;
}
libc::abort();
}
for pfd in pfds {
if pfd.revents & libc::POLLNVAL == 0 {
continue;
}
if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
// If the stream is closed but we failed to reopen it, abort the
// process. Otherwise we wouldn't preserve the safety of
// operations on the corresponding Rust object Stdin, Stdout, or
// Stderr.
libc::abort();
}
}
}
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))]
unsafe fn sanitize_standard_fds() {
use crate::sys::os::errno;
for fd in 0..3 {
if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
libc::abort();
}
}
}
}
#[cfg(any(
// The standard fds are always available in Miri.
miri,
target_os = "emscripten",
target_os = "fuchsia"))]
unsafe fn sanitize_standard_fds() {}

#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))]
unsafe fn reset_sigpipe() {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
Expand Down
69 changes: 69 additions & 0 deletions src/test/ui/closed-std-fds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Verifies that std provides replacement for the standard file descriptors when they are missing.
//
// run-pass
// ignore-windows unix specific test
// ignore-cloudabi no processes
// ignore-emscripten no processes
// ignore-sgx no processes

#![feature(rustc_private)]
extern crate libc;

use std::io::{self, Read};
use std::os::unix::process::CommandExt;
use std::process::Command;

fn main() {
let mut args = std::env::args();
let argv0 = args.next().expect("argv0");
match args.next().as_deref() {
Some("child") => child(),
None => parent(&argv0),
_ => unreachable!(),
}
}

fn parent(argv0: &str) {
let status = unsafe { Command::new(argv0)
.arg("child")
.pre_exec(close_std_fds_on_exec)
.status()
.expect("failed to execute child process")
};
if !status.success() {
panic!("child failed with {}", status);
}
}

fn close_std_fds_on_exec() -> io::Result<()> {
for fd in 0..3 {
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) == -1 } {
return Err(io::Error::last_os_error())
}
}
Ok(())
}

fn child() {
// Standard file descriptors should be valid.
assert_fd_is_valid(0);
assert_fd_is_valid(1);
assert_fd_is_valid(2);

// Writing to stdout & stderr should not panic.
println!("a");
println!("b");
eprintln!("c");
eprintln!("d");

// Stdin should be at EOF.
let mut buffer = Vec::new();
let n = io::stdin().read_to_end(&mut buffer).unwrap();
assert_eq!(n, 0);
}

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());
}
}