close_fds/
lib.rs

1//! # Why is this crate useful?
2//!
3//! By default, any file descriptors opened by a process are inherited by any of that process's
4//! children. This can cause major bugs and security issues, but this behavior can't be changed
5//! without massively breaking backwards compatibility.
6//!
7//! Rust hides most of these problems by setting the close-on-exec flag on all file descriptors
8//! opened by the standard library (which causes them to *not* be inherited by child processes).
9//! However, in some scenarios it may be necessary to have a way to close all open file
10//! descriptors.
11//!
12//! ## Scenarios where this is helpful
13//!
14//! - Writing set-UID programs (which may not be able to fully trust their environment; for
15//!   example, `sudo` closes all open file descriptors when it starts as a security measure)
16//! - Spawning processes while interacting with FFI code that *doesn't* set the close-on-exec flag
17//!   on file descriptors it opens (the functionality offered by this crate is the ONLY way to
18//!   safely do this)
19//! - On some platforms (notably, macOS/iOS), Rust isn't always able to set the close-on-exec flag
20//!   *atomically*, which creates race conditions if one thread is e.g. opening sockets while
21//!   another thread is spawning processes. This crate may be useful in helping to avoid those
22//!   race conditions.
23//!
24//! # Example usage
25//!
26//! Here is a short program that uses `close_fds` to close all file descriptors (except the ones
27//! in a specified list) in a child process before launching it:
28//!
29//! ```
30//! use std::process::Command;
31//! use std::os::unix::prelude::*;
32//!
33//! // Add any file descriptors that should stay open here
34//! let mut keep_fds = [];
35//! // ALWAYS sort the slice! It will give significant performance improvements.
36//! keep_fds.sort_unstable();
37//!
38//! let mut cmd = Command::new("true");
39//!
40//! // ...
41//! // Set up `cmd` here
42//! // ...
43//!
44//! unsafe {
45//!     cmd.pre_exec(move || {
46//!         // On macOS/iOS, just set them as close-on-exec (some sources indicate closing them
47//!         // directly may cause problems)
48//!         #[cfg(any(target_os = "macos", target_os = "ios"))]
49//!         close_fds::set_fds_cloexec(3, &keep_fds);
50//!         #[cfg(not(any(target_os = "macos", target_os = "ios")))]
51//!         close_fds::close_open_fds(3, &keep_fds);
52//!
53//!         Ok(())
54//!     });
55//! }
56//!
57//! // Launch the child process
58//!
59//! cmd.status().unwrap();
60//! ```
61//!
62//! # Builders vs. helper functions
63//!
64//! [`close_open_fds()`], [`set_fds_cloexec()`], [`iter_open_fds()`], etc. provide simple ways to
65//! close, set the close-on-exec flag on, or iterate over all open file descriptors.
66//!
67//! However, for more advanced use cases, there are also "builders" ([`CloseFdsBuilder`],
68//! [`FdIterBuilder`]) which can be used to further customize aspects of the actions that will be
69//! taken. The documentation of each helper function describes how the same task could be performed
70//! using one of the builders.
71//!
72//! # Async-signal-safety
73//!
74//! ## Background
75//!
76//! An async-signal-safe function is one that is safe to call from a signal handler (many functions
77//! in the system libc are not!). In a multi-threaded program, when running in the child after a
78//! `fork()` (such as in a closure registered with
79//! `std::os::unix::process::CommandExt::pre_exec()`, it is only safe to call async-signal-safe
80//! functions. See `signal-safety(7)` and `fork(2)` for more information.
81//!
82//! ## Async-signal-safety in this crate
83//!
84//! **TL;DR**: The functions in this crate are async-signal-safe on Linux, macOS/iOS, the BSDs, and
85//! Solaris/Illumos. They *should* also be async-signal-safe on other \*nix-like OSes.
86//!
87//! Since the functions in this crate are most useful in the child process after a `fork()`, this
88//! crate tries to make all of them async-signal-safe. However, many of the optimizations that this
89//! crate performs in order to be efficient would not be possible by simply calling functions that
90//! POSIX requires to be async-signal-safe.
91//!
92//! As a result, this crate assumes that the following functions are async-signal-safe (in addition
93//! to the ones required by POSIX):
94//!
95//! - `closefrom()` on the BSDs
96//! - The `close_range()` syscall on Linux and FreeBSD
97//! - `sysctl()` on FreeBSD
98//! - `getdtablecount()` on OpenBSD
99//! - `getdirentries()`/`getdents()` (whichever is available) on Linux, NetBSD, FreeBSD, macOS/iOS,
100//!   and Solaris/Illumos
101//! - `sysconf(_SC_OPEN_MAX)` on all OSes
102//!
103//! All of these except for `sysconf()` are implemented as system calls (or thin wrappers around
104//! other system calls) on whichever OS(es) they are present on. As a result, they should be
105//! async-signal-safe, even though they are not explicitly documented as such.
106//!
107//! `sysconf()` is not guaranteed to be async-signal-safe. However, on Linux, macOS/iOS, the BSDs,
108//! and Solaris/Illumos, `sysconf(_SC_OPEN_MAX)` is implemented in terms of
109//! `getrlimit(RLIMIT_NOFILE)`. On those platforms, `getrlimit()` is a system call, so
110//! `sysconf(_SC_OPEN_MAX)` (and thus, the functions in this crate) should be async-signal-safe.
111
112#![no_std]
113
114mod closefds;
115mod iterfds;
116mod sys;
117mod util;
118
119pub use closefds::*;
120pub use iterfds::*;