• A
    Add Linux-specific pidfd process extensions · 694be09b
    Aaron Hill 提交于
    Background:
    
    Over the last year, pidfd support was added to the Linux kernel. This
    allows interacting with other processes. In particular, this allows
    waiting on a child process with a timeout in a race-free way, bypassing
    all of the awful signal-handler tricks that are usually required.
    
    Pidfds can be obtained for a child process (as well as any other
    process) via the `pidfd_open` syscall. Unfortunately, this requires
    several conditions to hold in order to be race-free (i.e. the pid is not
    reused).
    Per `man pidfd_open`:
    
    ```
    · the disposition of SIGCHLD has not been explicitly set to SIG_IGN
     (see sigaction(2));
    
    · the SA_NOCLDWAIT flag was not specified while establishing a han‐
     dler for SIGCHLD or while setting the disposition of that signal to
     SIG_DFL (see sigaction(2)); and
    
    · the zombie process was not reaped elsewhere in the program (e.g.,
     either by an asynchronously executed signal handler or by wait(2)
     or similar in another thread).
    
    If any of these conditions does not hold, then the child process
    (along with a PID file descriptor that refers to it) should instead
    be created using clone(2) with the CLONE_PIDFD flag.
    ```
    
    Sadly, these conditions are impossible to guarantee once any libraries
    are used. For example, C code runnng in a different thread could call
    `wait()`, which is impossible to detect from Rust code trying to open a
    pidfd.
    
    While pid reuse issues should (hopefully) be rare in practice, we can do
    better. By passing the `CLONE_PIDFD` flag to `clone()` or `clone3()`, we
    can obtain a pidfd for the child process in a guaranteed race-free
    manner.
    
    This PR:
    
    This PR adds Linux-specific process extension methods to allow obtaining
    pidfds for processes spawned via the standard `Command` API. Other than
    being made available to user code, the standard library does not make
    use of these pidfds in any way. In particular, the implementation of
    `Child::wait` is completely unchanged.
    
    Two Linux-specific helper methods are added: `CommandExt::create_pidfd`
    and `ChildExt::pidfd`. These methods are intended to serve as a building
    block for libraries to build higher-level abstractions - in particular,
    waiting on a process with a timeout.
    
    I've included a basic test, which verifies that pidfds are created iff
    the `create_pidfd` method is used. This test is somewhat special - it
    should always succeed on systems with the `clone3` system call
    available, and always fail on systems without `clone3` available. I'm
    not sure how to best ensure this programatically.
    
    This PR relies on the newer `clone3` system call to pass the `CLONE_FD`,
    rather than the older `clone` system call. `clone3` was added to Linux
    in the same release as pidfds, so this shouldn't unnecessarily limit the
    kernel versions that this code supports.
    
    Unresolved questions:
    * What should the name of the feature gate be for these newly added
      methods?
    * Should the `pidfd` method distinguish between an error occurring
      and `create_pidfd` not being called?
    694be09b
process.rs 63.0 KB