[issue37549] os.dup() fails for standard streams on Windows 7

Eryk Sun report at bugs.python.org
Mon Jul 29 17:57:39 EDT 2019


Eryk Sun <eryksun at gmail.com> added the comment:

> It sounds like we should probably revert to the middle ground, and 
> skip _raising the error_ if _Py_set_inheritable for files of type
> FILE_TYPE_CHAR.

The problem is just console pseudohandles in Windows 7 and earlier.  Maybe it should just check for this case before calling SetHandleInformation. For example:

    /* This check can be removed once support for Windows 7 ends. */
    #define CONSOLE_PSEUDOHANDLE(handle) (((ULONG_PTR)(handle) & 0x3) == 0x3 && \
            GetFileType(handle) == FILE_TYPE_CHAR)

    if (!CONSOLE_PSEUDOHANDLE(handle) &&
        !SetHandleInformation(handle, HANDLE_FLAG_INHERIT, flags)) {
        if (raise)
            PyErr_SetFromWindowsErr(0);
        return -1;
    }

We have similar Python code in subprocess Popen._filter_handle_list, which filters out console pseudohandles because they don't work with PROC_THREAD_ATTRIBUTE_HANDLE_LIST.

A similar check could be added to os_set_handle_inheritable_impl in Modules/posixmodule.c.

> As Victor pointed out, the docs already call out the special case in 
> Windows. 

For os.dup it says

    On Windows, when duplicating a standard stream (0: stdin, 1: 
    stdout, 2: stderr), the new file descriptor is inheritable.

The standard handles aren't relevant.

Also, under "inheritance of file descriptors", it says:

    On Windows, non-inheritable handles and file descriptors are closed 
    in child processes, except for standard streams (file descriptors 0,
    1 and 2: stdin, stdout and stderr), which are always inherited.

The standard handles aren't always inherited. If bInheritHandles is TRUE (i.e. close_fds=False), a standard handle has to be iheritable for it to be inherited. 

For example, in Windows 10, make stderr (a ConDrv console handle) non-inheritable and create a child process with close_fds=False:

    >>> os.set_handle_inheritable(msvcrt.get_osfhandle(2), False)
    >>> cmd = 'import sys; print(sys.stderr)'
    >>> subprocess.call(f'python.exe -c "{cmd}"', close_fds=False)
    None
    0

In Windows 7 and earlier, it seems that inheritable console pseudohandles are always inherited, regardless of bInheritHandles -- as long as the child process attaches to the parent's console. SetHandleInformation isn't supported for console pseudohandles, but the inheritable flag is still set or cleared when a console pseudohandle is created via CreateConsoleScreenBuffer, CreateFileW (routed to OpenConsoleW), and DuplicateHandle (routed to DuplicateConsoleHandle).

It's worth mentioning that the system sometimes duplicates (not inherits) standard handles to a child process. A simple example in Windows 10 would be subprocess.call('python.exe'). 

All of the follow requirements must be satisfied for CreateProcessW to duplicate a standard handle to a child:

* it's not a console pseudohandle (e.g. it's a ConDrv console handle in 
  Windows 8+, or handle for the NUL device, a pipe, or disk file)
* the target executable is a console application (e.g. python.exe, not 
  pythonw.exe)
* handle inheritance is disabled (i.e. bInheritHandles is FALSE)
* the startup-info standard handles aren't used (i.e. 
  STARTF_USESTDHANDLES isn't set)
* the call isn't flagged to execute without a console or to allocate a
  new console (i.e. no DETACHED_PROCESS, CREATE_NEW_CONSOLE, or 
  CREATE_NO_WINDOW)

In this case, CreateProcessW also has to update the handle value in the child since generally the duplicate has a new value. With inheritance, in contrast, the handle value is the same in the parent and child.

----------

_______________________________________
Python tracker <report at bugs.python.org>
<https://bugs.python.org/issue37549>
_______________________________________


More information about the Python-bugs-list mailing list