Eryk Sun added the comment:

> when I said "also with close_fds=True", I meant that I tried 
> WITHOUT overriding stdin, stdout, and stderr AND setting 
> close_fds=True, but it didn't work. 

Console applications (e.g. python.exe) duplicate their standard handles into a 
child console process when the bInheritHandles argument of CreateProcess is 
false, i.e. when subprocess.Popen is called with the default close_fds=True. We 
know it's duplication instead of inheritance because the actual handle values 
change and also because it doesn't matter whether or not the standard handles 
are flagged as inheritable in the creating process. In the typical case this 
ensures that console processes have valid standard handles. 

However, if you make the standard handles non-inheritable in the parent and 
also inherit handles when creating the child, then the parent's standard 
handles will be neither inherited nor duplicated. The child will still inherit 
the standard handle *values* from the parent (i.e. copied from the parent's PEB 
ProcessParameters). These handle values will be invalid. Either the handle 
won't be defined, or it will reference an arbitrary, unrelated kernel object.

> What worked was not overriding stdin/out/err and adding
>
> os.set_inheritable(0, False)
> os.set_inheritable(1, False)
> os.set_inheritable(2, False)
>
> before the call (no need to set close_fds)

In this case cmd.exe gets duplicated standard handles, but you've flagged these 
handles as non-inheritable. When cmd.exe calls CreateProcess to execute 
waitfor.exe, it uses bInheritHandles=TRUE. Consequently, its non-inheritable 
standard handles are neither inherited by nor duplicated to waitfor.exe. Let's 
attach a debugger to the waitfor.exe process and examine the standard handle 
values in its PEB ProcessParameters:

    0:003> ?? @$peb->ProcessParameters->StandardInput
    void * 0x00000000`00000008
    0:003> !handle 8
    Handle 8
      Type          Event

    0:003> ?? @$peb->ProcessParameters->StandardOutput
    void * 0x00000000`0000000c
    0:003> !handle c
    Handle c
      Type          WaitCompletionPacket

    0:003> ?? @$peb->ProcessParameters->StandardError
    void * 0x00000000`00000010
    0:003> !handle 10
    Handle 10
      Type          IoCompletion

waitfor.exe doesn't care that its standard handle values reference non-File 
objects, i.e. an Event, a WaitCompletionPacket, and an IoCompletion port. 
However, what if by coincidence one of its standard handle values is actually a 
valid File handle that's inherited for some other reason? That would be a right 
mess. That's why I suggested overriding the standard handles to the NUL device. 
For example:

    import subprocess

    script = r'''
    import os
    import subprocess

    os.set_inheritable(0, False)
    os.set_inheritable(1, False)
    os.set_inheritable(2, False)

    cmdline = 'cmd.exe /s /c waitfor.exe signal /t 200'
    subprocess.run(cmdline, timeout=4, stdin=subprocess.DEVNULL,
        stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    '''

    args = ['python.exe', '-c', script]
    proc = subprocess.Popen(args, encoding='ansi', stdin=subprocess.PIPE,
                stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    proc_out, proc_err = proc.communicate(timeout=20)
    print('proc_out:', proc_out)
    print('proc_err:', proc_err)

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue31447>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to