mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Issue #12786: Set communication pipes used by subprocess.Popen CLOEXEC to avoid
them being inherited by other subprocesses.
This commit is contained in:
parent
eacada8656
commit
2a34eb3104
3 changed files with 50 additions and 6 deletions
|
@ -1035,7 +1035,7 @@ class Popen(object):
|
||||||
if stdin is None:
|
if stdin is None:
|
||||||
pass
|
pass
|
||||||
elif stdin == PIPE:
|
elif stdin == PIPE:
|
||||||
p2cread, p2cwrite = os.pipe()
|
p2cread, p2cwrite = self.pipe_cloexec()
|
||||||
elif isinstance(stdin, int):
|
elif isinstance(stdin, int):
|
||||||
p2cread = stdin
|
p2cread = stdin
|
||||||
else:
|
else:
|
||||||
|
@ -1045,7 +1045,7 @@ class Popen(object):
|
||||||
if stdout is None:
|
if stdout is None:
|
||||||
pass
|
pass
|
||||||
elif stdout == PIPE:
|
elif stdout == PIPE:
|
||||||
c2pread, c2pwrite = os.pipe()
|
c2pread, c2pwrite = self.pipe_cloexec()
|
||||||
elif isinstance(stdout, int):
|
elif isinstance(stdout, int):
|
||||||
c2pwrite = stdout
|
c2pwrite = stdout
|
||||||
else:
|
else:
|
||||||
|
@ -1055,7 +1055,7 @@ class Popen(object):
|
||||||
if stderr is None:
|
if stderr is None:
|
||||||
pass
|
pass
|
||||||
elif stderr == PIPE:
|
elif stderr == PIPE:
|
||||||
errread, errwrite = os.pipe()
|
errread, errwrite = self.pipe_cloexec()
|
||||||
elif stderr == STDOUT:
|
elif stderr == STDOUT:
|
||||||
errwrite = c2pwrite
|
errwrite = c2pwrite
|
||||||
elif isinstance(stderr, int):
|
elif isinstance(stderr, int):
|
||||||
|
@ -1082,6 +1082,18 @@ class Popen(object):
|
||||||
fcntl.fcntl(fd, fcntl.F_SETFD, old & ~cloexec_flag)
|
fcntl.fcntl(fd, fcntl.F_SETFD, old & ~cloexec_flag)
|
||||||
|
|
||||||
|
|
||||||
|
def pipe_cloexec(self):
|
||||||
|
"""Create a pipe with FDs set CLOEXEC."""
|
||||||
|
# Pipes' FDs are set CLOEXEC by default because we don't want them
|
||||||
|
# to be inherited by other subprocesses: the CLOEXEC flag is removed
|
||||||
|
# from the child's FDs by _dup2(), between fork() and exec().
|
||||||
|
# This is not atomic: we would need the pipe2() syscall for that.
|
||||||
|
r, w = os.pipe()
|
||||||
|
self._set_cloexec_flag(r)
|
||||||
|
self._set_cloexec_flag(w)
|
||||||
|
return r, w
|
||||||
|
|
||||||
|
|
||||||
def _close_fds(self, but):
|
def _close_fds(self, but):
|
||||||
if hasattr(os, 'closerange'):
|
if hasattr(os, 'closerange'):
|
||||||
os.closerange(3, but)
|
os.closerange(3, but)
|
||||||
|
@ -1120,11 +1132,9 @@ class Popen(object):
|
||||||
# For transferring possible exec failure from child to parent
|
# For transferring possible exec failure from child to parent
|
||||||
# The first char specifies the exception type: 0 means
|
# The first char specifies the exception type: 0 means
|
||||||
# OSError, 1 means some other error.
|
# OSError, 1 means some other error.
|
||||||
errpipe_read, errpipe_write = os.pipe()
|
errpipe_read, errpipe_write = self.pipe_cloexec()
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
self._set_cloexec_flag(errpipe_write)
|
|
||||||
|
|
||||||
gc_was_enabled = gc.isenabled()
|
gc_was_enabled = gc.isenabled()
|
||||||
# Disable gc to avoid bug where gc -> file_dealloc ->
|
# Disable gc to avoid bug where gc -> file_dealloc ->
|
||||||
# write to stderr -> hang. http://bugs.python.org/issue1336
|
# write to stderr -> hang. http://bugs.python.org/issue1336
|
||||||
|
|
|
@ -995,6 +995,37 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||||
self.assertRaises(OSError, os.waitpid, pid, 0)
|
self.assertRaises(OSError, os.waitpid, pid, 0)
|
||||||
self.assertNotIn(ident, [id(o) for o in subprocess._active])
|
self.assertNotIn(ident, [id(o) for o in subprocess._active])
|
||||||
|
|
||||||
|
def test_pipe_cloexec(self):
|
||||||
|
# Issue 12786: check that the communication pipes' FDs are set CLOEXEC,
|
||||||
|
# and are not inherited by another child process.
|
||||||
|
p1 = subprocess.Popen([sys.executable, "-c",
|
||||||
|
'import os;'
|
||||||
|
'os.read(0, 1)'
|
||||||
|
],
|
||||||
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
|
||||||
|
p2 = subprocess.Popen([sys.executable, "-c", """if True:
|
||||||
|
import os, errno, sys
|
||||||
|
for fd in %r:
|
||||||
|
try:
|
||||||
|
os.close(fd)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EBADF:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
sys.exit(0)
|
||||||
|
""" % [f.fileno() for f in (p1.stdin, p1.stdout,
|
||||||
|
p1.stderr)]
|
||||||
|
],
|
||||||
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE, close_fds=False)
|
||||||
|
p1.communicate('foo')
|
||||||
|
_, stderr = p2.communicate()
|
||||||
|
|
||||||
|
self.assertEqual(p2.returncode, 0, "Unexpected error: " + repr(stderr))
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(mswindows, "Windows specific tests")
|
@unittest.skipUnless(mswindows, "Windows specific tests")
|
||||||
class Win32ProcessTestCase(BaseTestCase):
|
class Win32ProcessTestCase(BaseTestCase):
|
||||||
|
|
|
@ -40,6 +40,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #12786: Set communication pipes used by subprocess.Popen CLOEXEC to
|
||||||
|
avoid them being inherited by other subprocesses.
|
||||||
|
|
||||||
- Issue #4106: Fix occasional exceptions printed out by multiprocessing on
|
- Issue #4106: Fix occasional exceptions printed out by multiprocessing on
|
||||||
interpreter shutdown.
|
interpreter shutdown.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue