-
Notifications
You must be signed in to change notification settings - Fork 207
Description
Following up on a discussion in #97: std::process::Command
has a safe API for spawning child processes. Its documentation doesn't mention non-O_CLOEXEC
file descriptors, but from some experimenting, child processes do inherit non-O_CLOEXEC
file descriptors.
Does this violate I/O safety?
When a program calls Command::spawn
, all existing memory in the process is replaced with a new program image, so Rust's safety guarantees have typically considered that to be the end of one program and the beginning of a new one. However, this is an area where file descriptors are different from pointers. FIle descriptors not marked O_CLOEXEC
persist into the next program. In effect, spawn
is implicitly obtaining the values of all such file descriptors in the process, even those meant to be encapsulated, and "passing" them to another program where they could be accessed. That has the shape of an I/O safety violation. And in practice, leaking file descriptors to child processes is a real security problem.
One possible way out of this is to say that safe code can't create non-O_CLOEXEC
file descriptors. Rust's std already does open all file descriptors as O_CLOEXEC
whenever it can, and sets them to O_CLOEXEC
even when it can't create them that way. Perhaps then, rustix's openat
function should always set O_CLOEXEC
too, and perhaps there should be a special function named something like unsafe fn dup2_no_cloexec
for creating non-O_CLOEXEC
file descriptors for when those are really needed.
One tricky issue is that not all OS's can set O_CLOEXEC
atomically in all cases. Rust uses ioctl
with FIOCLEX
to set the CLOEXEC
flag as soon as it can, but there is a window where another thread could call exec
and capture the file descriptor before it has O_CLOEXEC
set. A related issue is that users of std::process::Command
may already be creating non-O_CLOEXEC
file descriptors within safe functions. A possible long-term path out of this is to say that post-exec
programs accessing file descriptors passed through exec
is in the same category as using a debugger on the pre-exec
program, so it's out of scope for I/O safety, and then to provide a new safe way to pass file descriptors to child processes and gradually encourage the Rust ecosystem to adopt it.