Kaydet (Commit) 10706e28 authored tarafından Facundo Batista's avatar Facundo Batista

Issue #6274. Fixed a potential FD leak in subprocess.py.

üst e9ff86e3
...@@ -1034,91 +1034,102 @@ class Popen(object): ...@@ -1034,91 +1034,102 @@ class Popen(object):
# 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 = os.pipe()
self._set_cloexec_flag(errpipe_write)
gc_was_enabled = gc.isenabled()
# Disable gc to avoid bug where gc -> file_dealloc ->
# write to stderr -> hang. http://bugs.python.org/issue1336
gc.disable()
try: try:
self.pid = os.fork()
except:
if gc_was_enabled:
gc.enable()
raise
self._child_created = True
if self.pid == 0:
# Child
try: try:
# Close parent's pipe ends self._set_cloexec_flag(errpipe_write)
if p2cwrite is not None:
os.close(p2cwrite) gc_was_enabled = gc.isenabled()
if c2pread is not None: # Disable gc to avoid bug where gc -> file_dealloc ->
os.close(c2pread) # write to stderr -> hang. http://bugs.python.org/issue1336
if errread is not None: gc.disable()
os.close(errread) try:
os.close(errpipe_read) self.pid = os.fork()
except:
# Dup fds for child if gc_was_enabled:
if p2cread is not None: gc.enable()
os.dup2(p2cread, 0) raise
if c2pwrite is not None: self._child_created = True
os.dup2(c2pwrite, 1) if self.pid == 0:
if errwrite is not None: # Child
os.dup2(errwrite, 2) try:
# Close parent's pipe ends
# Close pipe fds. Make sure we don't close the same if p2cwrite is not None:
# fd more than once, or standard fds. os.close(p2cwrite)
if p2cread is not None and p2cread not in (0,): if c2pread is not None:
os.close(p2cread) os.close(c2pread)
if c2pwrite is not None and c2pwrite not in (p2cread, 1): if errread is not None:
os.close(c2pwrite) os.close(errread)
if (errwrite is not None and os.close(errpipe_read)
errwrite not in (p2cread, c2pwrite, 2)):
os.close(errwrite) # Dup fds for child
if p2cread is not None:
# Close all other fds, if asked for os.dup2(p2cread, 0)
if close_fds: if c2pwrite is not None:
self._close_fds(but=errpipe_write) os.dup2(c2pwrite, 1)
if errwrite is not None:
if cwd is not None: os.dup2(errwrite, 2)
os.chdir(cwd)
# Close pipe fds. Make sure we don't close the
if preexec_fn: # same fd more than once, or standard fds.
preexec_fn() if p2cread is not None and p2cread not in (0,):
os.close(p2cread)
if env is None: if c2pwrite is not None and \
os.execvp(executable, args) c2pwrite not in (p2cread, 1):
else: os.close(c2pwrite)
os.execvpe(executable, args, env) if (errwrite is not None and
errwrite not in (p2cread, c2pwrite, 2)):
except: os.close(errwrite)
exc_type, exc_value, tb = sys.exc_info()
# Save the traceback and attach it to the exception object # Close all other fds, if asked for
exc_lines = traceback.format_exception(exc_type, if close_fds:
exc_value, self._close_fds(but=errpipe_write)
tb)
exc_value.child_traceback = ''.join(exc_lines) if cwd is not None:
os.write(errpipe_write, pickle.dumps(exc_value)) os.chdir(cwd)
# This exitcode won't be reported to applications, so it if preexec_fn:
# really doesn't matter what we return. preexec_fn()
os._exit(255)
if env is None:
# Parent os.execvp(executable, args)
if gc_was_enabled: else:
gc.enable() os.execvpe(executable, args, env)
os.close(errpipe_write)
if p2cread is not None and p2cwrite is not None: except:
os.close(p2cread) exc_type, exc_value, tb = sys.exc_info()
if c2pwrite is not None and c2pread is not None: # Save the traceback and attach it to the exception
os.close(c2pwrite) # object
if errwrite is not None and errread is not None: exc_lines = traceback.format_exception(exc_type,
os.close(errwrite) exc_value,
tb)
# Wait for exec to fail or succeed; possibly raising exception exc_value.child_traceback = ''.join(exc_lines)
data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB os.write(errpipe_write, pickle.dumps(exc_value))
os.close(errpipe_read)
# This exitcode won't be reported to applications, so
# it really doesn't matter what we return.
os._exit(255)
# Parent
if gc_was_enabled:
gc.enable()
finally:
# be sure the FD is closed no matter what
os.close(errpipe_write)
if p2cread is not None and p2cwrite is not None:
os.close(p2cread)
if c2pwrite is not None and c2pread is not None:
os.close(c2pwrite)
if errwrite is not None and errread is not None:
os.close(errwrite)
# Wait for exec to fail or succeed; possibly raising an
# exception (limited to 1 MB)
data = os.read(errpipe_read, 1048576)
finally:
# be sure the FD is closed no matter what
os.close(errpipe_read)
if data: if data:
os.waitpid(self.pid, 0) os.waitpid(self.pid, 0)
child_exception = pickle.loads(data) child_exception = pickle.loads(data)
......
...@@ -15,6 +15,8 @@ Core and Builtins ...@@ -15,6 +15,8 @@ Core and Builtins
Library Library
------- -------
- Issue #6274: Fixed possible file descriptors leak in subprocess.py
- Accessing io.StringIO.buffer now raises an AttributeError instead of - Accessing io.StringIO.buffer now raises an AttributeError instead of
io.UnsupportedOperation. io.UnsupportedOperation.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment