Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

windows: when creating process with redirected stdio, make parent's std handles not inheritable #54760

Open
ikostia opened this issue Oct 2, 2018 · 6 comments
Labels
A-process Area: std::process and std::env O-windows Operating system: Windows

Comments

@ikostia
Copy link

ikostia commented Oct 2, 2018

Currently, if this happens:

use std::process::Command;
...
Command::new("child.exe")
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()

the child.exe accidentally inherits stdin, stdout and stderr handles of the parent process. This can be bad, because the objects these handles point to (say pipes), may be watched by some third process.

Since at the point of creating a child process we know that it does not need our std handles (because it's explicitly being created with Stdio::null()), we can temporarily disable handle inheritance on these handles.

@estebank estebank added the O-windows Operating system: Windows label Oct 3, 2018
@retep998
Copy link
Member

Mucking about with enabling and disabling inheritance on handles is inherently racy and a bad idea.

We really just need to implement the solution in https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873

@ikostia
Copy link
Author

ikostia commented Nov 14, 2018

@retep998: you are talking about #38227, right?

The problem I personally have with that approach is that it may be very hard to know which handles are intended to be inheritable and thus need to be passed in STARTUPINFOEX.

I will ask a question under that issue to keep the discussion centralized.

@DemiMarie
Copy link
Contributor

@ikostia Only handles expressly passed to a child process should be inherited. The rest should not. This also applies to other platforms.

@retep998
Copy link
Member

retep998 commented Apr 7, 2020

This can be trivially solved if #38227 were implemented.

@JamesMc86
Copy link

I wanted to add a failure mode which made this important to me.

I was trying to use command::spawn to start a process that outlives mine. It works from console but if stdout is piped then my process is held open until the spawned process is complete - breaking the expected behaviour of spawn.

Calling the following function operates as a workaround for now:

#[cfg(target_os = "windows")]
/// Disables inheritance on the inout pipe handles.
fn disable_handle_inheritance() {
    use windows::Win32::Foundation::{SetHandleInformation, HANDLE_FLAGS, HANDLE_FLAG_INHERIT};
    use windows::Win32::System::Console::{
        GetStdHandle, STD_ERROR_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
    };

    unsafe {
        let std_err = GetStdHandle(STD_ERROR_HANDLE);
        let std_in = GetStdHandle(STD_INPUT_HANDLE);
        let std_out = GetStdHandle(STD_OUTPUT_HANDLE);

        for handle in [std_err, std_in, std_out] {
            SetHandleInformation(handle, HANDLE_FLAG_INHERIT.0, HANDLE_FLAGS(0));
        }
    }
}

facebook-github-bot pushed a commit to facebook/buck2 that referenced this issue May 26, 2022
Summary:
When test runner waits for `buck2` process it hangs because buckd process is still running.

This happens because buckd inherits handles from parent process and wait on the parent process awaits for these handles to be closed.

Example: https://stackoverflow.com/questions/13243807/popen-waiting-for-child-process-even-when-the-immediate-child-has-terminated

`Popen` in Python has `close_fds` argument to solve this:

> On Windows, if close_fds is true then no handles will be inherited by the child process unless explicitly passed in the handle_list element of STARTUPINFO.lpAttributeList, or by standard handle redirection.
https://docs.python.org/3/library/subprocess.html

Rust sets `bInheritHandles` to `TRUE` unconditionally: https://github.com/rust-lang/rust/blob/acb5c16fa8acf7fd3b48fc218881f006577bab1a/library/std/src/sys/windows/process.rs#L327

There are several open issues related to this:
- rust-lang/rust#54760
- rust-lang/rust#38227

Because of that we have to call `CreateProcessW` ourselves.

Implementation is inspired by:
- std library: https://github.com/rust-lang/rust/blob/master/library/std/src/sys/windows/process.rs
- subprocess library: https://github.com/hniksic/rust-subprocess/blob/master/src/win32.rs
- docs for `CreateProcessW`: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw

Reviewed By: stepancheg

Differential Revision: D36663797

fbshipit-source-id: 14431002a763042f9c160b8a7ff7f8a62dd6befa
@workingjubilee workingjubilee added the A-process Area: std::process and std::env label Mar 19, 2023
@jsturtevant
Copy link

I was trying to use command::spawn to start a process that outlives mine. It works from console but if stdout is piped then my process is held open until the spawned process is complete - breaking the expected behaviour of spawn.

I ran into this as well. Adding another example where this is an issue.

In containerd a shim process is created and run to completion listening to the stdout. That process is often implemented to be short lived and responsible for some set up, and then it should create a new long lived process. Because containerd is listening to STDOUT this caused containerd to hang when spawning the long lived process. Even though the short live process completed, the inherited handle was causing a hang until the new child process completed.

The work around above did help. The issue seems to come from always passing true for bInheritHandles to createprocess:

cvt(c::CreateProcessW(
program.as_ptr(),
cmd_str.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
c::TRUE,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-process Area: std::process and std::env O-windows Operating system: Windows
Projects
None yet
Development

No branches or pull requests

7 participants