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 <[email protected]>
<https://bugs.python.org/issue31447>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com