summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFacundo Batista <facundobatista@gmail.com>2009-06-19 20:34:30 (GMT)
committerFacundo Batista <facundobatista@gmail.com>2009-06-19 20:34:30 (GMT)
commit10706e28d33905d7e949c4f6544d182759c8ebc2 (patch)
treed76d696dd4dde3815bf1a7324def1daee26ab136
parente9ff86e3a16c261fadca15716e5c8ffb75cd4dfc (diff)
downloadcpython-10706e28d33905d7e949c4f6544d182759c8ebc2.zip
cpython-10706e28d33905d7e949c4f6544d182759c8ebc2.tar.gz
cpython-10706e28d33905d7e949c4f6544d182759c8ebc2.tar.bz2
Issue #6274. Fixed a potential FD leak in subprocess.py.
-rw-r--r--Lib/subprocess.py177
-rw-r--r--Misc/NEWS2
2 files changed, 96 insertions, 83 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 638058e..368ba0e 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -1034,91 +1034,102 @@ class Popen(object):
# The first char specifies the exception type: 0 means
# OSError, 1 means some other error.
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:
- self.pid = os.fork()
- 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)
-
- # Dup fds for child
- if p2cread is not None:
- os.dup2(p2cread, 0)
- if c2pwrite is not None:
- os.dup2(c2pwrite, 1)
- if errwrite is not None:
- os.dup2(errwrite, 2)
-
- # Close pipe fds. Make sure we don't close the same
- # fd more than once, or standard fds.
- 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)
-
- # Close all other fds, if asked for
- if close_fds:
- self._close_fds(but=errpipe_write)
-
- if cwd is not None:
- os.chdir(cwd)
-
- if preexec_fn:
- preexec_fn()
-
- if env is None:
- os.execvp(executable, args)
- else:
- os.execvpe(executable, args, env)
-
- except:
- exc_type, exc_value, tb = sys.exc_info()
- # 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))
-
- # 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()
- 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) # Exceptions limited to 1 MB
- os.close(errpipe_read)
+ 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:
+ self.pid = os.fork()
+ 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)
+
+ # Dup fds for child
+ if p2cread is not None:
+ os.dup2(p2cread, 0)
+ if c2pwrite is not None:
+ os.dup2(c2pwrite, 1)
+ if errwrite is not None:
+ os.dup2(errwrite, 2)
+
+ # Close pipe fds. Make sure we don't close the
+ # same fd more than once, or standard fds.
+ 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)
+
+ # Close all other fds, if asked for
+ if close_fds:
+ self._close_fds(but=errpipe_write)
+
+ if cwd is not None:
+ os.chdir(cwd)
+
+ if preexec_fn:
+ preexec_fn()
+
+ if env is None:
+ os.execvp(executable, args)
+ else:
+ os.execvpe(executable, args, env)
+
+ except:
+ exc_type, exc_value, tb = sys.exc_info()
+ # 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))
+
+ # 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:
os.waitpid(self.pid, 0)
child_exception = pickle.loads(data)
diff --git a/Misc/NEWS b/Misc/NEWS
index 9ccb472..9adcfdf 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,8 @@ Core and Builtins
Library
-------
+- Issue #6274: Fixed possible file descriptors leak in subprocess.py
+
- Accessing io.StringIO.buffer now raises an AttributeError instead of
io.UnsupportedOperation.