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

This commit is contained in:
Facundo Batista 2009-06-19 18:02:28 +00:00
parent 5fe420e34c
commit 8c826b77e0
2 changed files with 82 additions and 72 deletions

View File

@ -1056,90 +1056,98 @@ 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)
if c2pread is not None:
os.close(c2pread)
if errread is not None:
os.close(errread)
os.close(errpipe_read)
# Dup fds for child gc_was_enabled = gc.isenabled()
if p2cread is not None: # Disable gc to avoid bug where gc -> file_dealloc ->
os.dup2(p2cread, 0) # write to stderr -> hang. http://bugs.python.org/issue1336
if c2pwrite is not None: gc.disable()
os.dup2(c2pwrite, 1) try:
if errwrite is not None: self.pid = os.fork()
os.dup2(errwrite, 2) except:
if gc_was_enabled:
gc.enable()
raise
self._child_created = True
if self.pid == 0:
# Child
try:
# Close parent's pipe ends
if p2cwrite is not None:
os.close(p2cwrite)
if c2pread is not None:
os.close(c2pread)
if errread is not None:
os.close(errread)
os.close(errpipe_read)
# Close pipe fds. Make sure we don't close the same # Dup fds for child
# fd more than once, or standard fds. if p2cread is not None:
if p2cread is not None and p2cread not in (0,): os.dup2(p2cread, 0)
os.close(p2cread) if c2pwrite is not None:
if c2pwrite is not None and c2pwrite not in (p2cread, 1): os.dup2(c2pwrite, 1)
os.close(c2pwrite) if errwrite is not None:
if errwrite is not None and errwrite not in (p2cread, c2pwrite, 2): os.dup2(errwrite, 2)
os.close(errwrite)
# Close all other fds, if asked for # Close pipe fds. Make sure we don't close the same
if close_fds: # fd more than once, or standard fds.
self._close_fds(but=errpipe_write) if p2cread is not None and p2cread not in (0,):
os.close(p2cread)
if c2pwrite is not None and c2pwrite not in (p2cread, 1):
os.close(c2pwrite)
if errwrite is not None and errwrite not in (p2cread, c2pwrite, 2):
os.close(errwrite)
if cwd is not None: # Close all other fds, if asked for
os.chdir(cwd) if close_fds:
self._close_fds(but=errpipe_write)
if preexec_fn: if cwd is not None:
preexec_fn() os.chdir(cwd)
if env is None: if preexec_fn:
os.execvp(executable, args) preexec_fn()
else:
os.execvpe(executable, args, env)
except: if env is None:
exc_type, exc_value, tb = sys.exc_info() os.execvp(executable, args)
# Save the traceback and attach it to the exception object else:
exc_lines = traceback.format_exception(exc_type, os.execvpe(executable, args, env)
exc_value,
tb)
exc_value.child_traceback = ''.join(exc_lines)
os.write(errpipe_write, pickle.dumps(exc_value))
# This exitcode won't be reported to applications, so it except:
# really doesn't matter what we return. exc_type, exc_value, tb = sys.exc_info()
os._exit(255) # Save the traceback and attach it to the exception object
exc_lines = traceback.format_exception(exc_type,
exc_value,
tb)
exc_value.child_traceback = ''.join(exc_lines)
os.write(errpipe_write, pickle.dumps(exc_value))
# Parent # This exitcode won't be reported to applications, so it
if gc_was_enabled: # really doesn't matter what we return.
gc.enable() os._exit(255)
os.close(errpipe_write)
if p2cread is not None and p2cwrite is not None: # Parent
os.close(p2cread) if gc_was_enabled:
if c2pwrite is not None and c2pread is not None: gc.enable()
os.close(c2pwrite) finally:
if errwrite is not None and errread is not None: # be sure the FD is closed no matter what
os.close(errwrite) 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 exception
data = os.read(errpipe_read, 1048576) # Exception limited to 1M
finally:
# be sure the FD is closed no matter what
os.close(errpipe_read)
# Wait for exec to fail or succeed; possibly raising exception
data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB
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)

View File

@ -327,6 +327,8 @@ Core and Builtins
Library Library
------- -------
- Issue #6274: Fixed possible file descriptors leak in subprocess.py
- Issue #6189: Restored compatibility of subprocess.py with Python 2.2. - Issue #6189: Restored compatibility of subprocess.py with Python 2.2.
- Issue #6287: Added the license field in Distutils documentation. - Issue #6287: Added the license field in Distutils documentation.