Gentoo Archives: gentoo-commits

From: "Mike Gilbert (floppym)" <floppym@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] gentoo-x86 commit in dev-lang/python/files: python-2.7-issue18851.patch
Date: Sat, 26 Oct 2013 15:11:28
Message-Id: 20131026151120.C6E5020047@flycatcher.gentoo.org
1 floppym 13/10/26 15:11:20
2
3 Added: python-2.7-issue18851.patch
4 Log:
5 Apply patch to fix regression in subprocess module, bug 489378.
6
7 (Portage version: 2.2.7/cvs/Linux x86_64, signed Manifest commit with key 0BBEEA1FEA4843A4)
8
9 Revision Changes Path
10 1.1 dev-lang/python/files/python-2.7-issue18851.patch
11
12 file : http://sources.gentoo.org/viewvc.cgi/gentoo-x86/dev-lang/python/files/python-2.7-issue18851.patch?rev=1.1&view=markup
13 plain: http://sources.gentoo.org/viewvc.cgi/gentoo-x86/dev-lang/python/files/python-2.7-issue18851.patch?rev=1.1&content-type=text/plain
14
15 Index: python-2.7-issue18851.patch
16 ===================================================================
17 # HG changeset patch
18 # User Antoine Pitrou <solipsis@××××××.net>
19 # Date 1377898693 -7200
20 # Node ID 43749cb6bdbd0fdab70f76cd171c3c02a3f600dd
21 # Parent ba54011aa295004ad87438211fe3bb1568dd69ab
22 Issue #18851: Avoid a double close of subprocess pipes when the child process fails starting.
23
24 diff --git a/Lib/subprocess.py b/Lib/subprocess.py
25 --- a/Lib/subprocess.py
26 +++ b/Lib/subprocess.py
27 @@ -698,12 +698,12 @@ class Popen(object):
28
29 (p2cread, p2cwrite,
30 c2pread, c2pwrite,
31 - errread, errwrite) = self._get_handles(stdin, stdout, stderr)
32 + errread, errwrite), to_close = self._get_handles(stdin, stdout, stderr)
33
34 try:
35 self._execute_child(args, executable, preexec_fn, close_fds,
36 cwd, env, universal_newlines,
37 - startupinfo, creationflags, shell,
38 + startupinfo, creationflags, shell, to_close,
39 p2cread, p2cwrite,
40 c2pread, c2pwrite,
41 errread, errwrite)
42 @@ -711,18 +711,12 @@ class Popen(object):
43 # Preserve original exception in case os.close raises.
44 exc_type, exc_value, exc_trace = sys.exc_info()
45
46 - to_close = []
47 - # Only close the pipes we created.
48 - if stdin == PIPE:
49 - to_close.extend((p2cread, p2cwrite))
50 - if stdout == PIPE:
51 - to_close.extend((c2pread, c2pwrite))
52 - if stderr == PIPE:
53 - to_close.extend((errread, errwrite))
54 -
55 for fd in to_close:
56 try:
57 - os.close(fd)
58 + if mswindows:
59 + fd.Close()
60 + else:
61 + os.close(fd)
62 except EnvironmentError:
63 pass
64
65 @@ -816,8 +810,9 @@ class Popen(object):
66 """Construct and return tuple with IO objects:
67 p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
68 """
69 + to_close = set()
70 if stdin is None and stdout is None and stderr is None:
71 - return (None, None, None, None, None, None)
72 + return (None, None, None, None, None, None), to_close
73
74 p2cread, p2cwrite = None, None
75 c2pread, c2pwrite = None, None
76 @@ -835,6 +830,10 @@ class Popen(object):
77 # Assuming file-like object
78 p2cread = msvcrt.get_osfhandle(stdin.fileno())
79 p2cread = self._make_inheritable(p2cread)
80 + # We just duplicated the handle, it has to be closed at the end
81 + to_close.add(p2cread)
82 + if stdin == PIPE:
83 + to_close.add(p2cwrite)
84
85 if stdout is None:
86 c2pwrite = _subprocess.GetStdHandle(_subprocess.STD_OUTPUT_HANDLE)
87 @@ -848,6 +847,10 @@ class Popen(object):
88 # Assuming file-like object
89 c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
90 c2pwrite = self._make_inheritable(c2pwrite)
91 + # We just duplicated the handle, it has to be closed at the end
92 + to_close.add(c2pwrite)
93 + if stdout == PIPE:
94 + to_close.add(c2pread)
95
96 if stderr is None:
97 errwrite = _subprocess.GetStdHandle(_subprocess.STD_ERROR_HANDLE)
98 @@ -863,10 +866,14 @@ class Popen(object):
99 # Assuming file-like object
100 errwrite = msvcrt.get_osfhandle(stderr.fileno())
101 errwrite = self._make_inheritable(errwrite)
102 + # We just duplicated the handle, it has to be closed at the end
103 + to_close.add(errwrite)
104 + if stderr == PIPE:
105 + to_close.add(errread)
106
107 return (p2cread, p2cwrite,
108 c2pread, c2pwrite,
109 - errread, errwrite)
110 + errread, errwrite), to_close
111
112
113 def _make_inheritable(self, handle):
114 @@ -895,7 +902,7 @@ class Popen(object):
115
116 def _execute_child(self, args, executable, preexec_fn, close_fds,
117 cwd, env, universal_newlines,
118 - startupinfo, creationflags, shell,
119 + startupinfo, creationflags, shell, to_close,
120 p2cread, p2cwrite,
121 c2pread, c2pwrite,
122 errread, errwrite):
123 @@ -934,6 +941,10 @@ class Popen(object):
124 # kill children.
125 creationflags |= _subprocess.CREATE_NEW_CONSOLE
126
127 + def _close_in_parent(fd):
128 + fd.Close()
129 + to_close.remove(fd)
130 +
131 # Start the process
132 try:
133 hp, ht, pid, tid = _subprocess.CreateProcess(executable, args,
134 @@ -958,11 +969,11 @@ class Popen(object):
135 # pipe will not close when the child process exits and the
136 # ReadFile will hang.
137 if p2cread is not None:
138 - p2cread.Close()
139 + _close_in_parent(p2cread)
140 if c2pwrite is not None:
141 - c2pwrite.Close()
142 + _close_in_parent(c2pwrite)
143 if errwrite is not None:
144 - errwrite.Close()
145 + _close_in_parent(errwrite)
146
147 # Retain the process handle, but close the thread handle
148 self._child_created = True
149 @@ -1088,6 +1099,7 @@ class Popen(object):
150 """Construct and return tuple with IO objects:
151 p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
152 """
153 + to_close = set()
154 p2cread, p2cwrite = None, None
155 c2pread, c2pwrite = None, None
156 errread, errwrite = None, None
157 @@ -1096,6 +1108,7 @@ class Popen(object):
158 pass
159 elif stdin == PIPE:
160 p2cread, p2cwrite = self.pipe_cloexec()
161 + to_close.update((p2cread, p2cwrite))
162 elif isinstance(stdin, int):
163 p2cread = stdin
164 else:
165 @@ -1106,6 +1119,7 @@ class Popen(object):
166 pass
167 elif stdout == PIPE:
168 c2pread, c2pwrite = self.pipe_cloexec()
169 + to_close.update((c2pread, c2pwrite))
170 elif isinstance(stdout, int):
171 c2pwrite = stdout
172 else:
173 @@ -1116,6 +1130,7 @@ class Popen(object):
174 pass
175 elif stderr == PIPE:
176 errread, errwrite = self.pipe_cloexec()
177 + to_close.update((errread, errwrite))
178 elif stderr == STDOUT:
179 errwrite = c2pwrite
180 elif isinstance(stderr, int):
181 @@ -1126,7 +1141,7 @@ class Popen(object):
182
183 return (p2cread, p2cwrite,
184 c2pread, c2pwrite,
185 - errread, errwrite)
186 + errread, errwrite), to_close
187
188
189 def _set_cloexec_flag(self, fd, cloexec=True):
190 @@ -1170,7 +1185,7 @@ class Popen(object):
191
192 def _execute_child(self, args, executable, preexec_fn, close_fds,
193 cwd, env, universal_newlines,
194 - startupinfo, creationflags, shell,
195 + startupinfo, creationflags, shell, to_close,
196 p2cread, p2cwrite,
197 c2pread, c2pwrite,
198 errread, errwrite):
199 @@ -1189,6 +1204,10 @@ class Popen(object):
200 if executable is None:
201 executable = args[0]
202
203 + def _close_in_parent(fd):
204 + os.close(fd)
205 + to_close.remove(fd)
206 +
207 # For transferring possible exec failure from child to parent
208 # The first char specifies the exception type: 0 means
209 # OSError, 1 means some other error.
210 @@ -1283,17 +1302,17 @@ class Popen(object):
211 # be sure the FD is closed no matter what
212 os.close(errpipe_write)
213
214 - if p2cread is not None and p2cwrite is not None:
215 - os.close(p2cread)
216 - if c2pwrite is not None and c2pread is not None:
217 - os.close(c2pwrite)
218 - if errwrite is not None and errread is not None:
219 - os.close(errwrite)
220 -
221 # Wait for exec to fail or succeed; possibly raising exception
222 # Exception limited to 1M
223 data = _eintr_retry_call(os.read, errpipe_read, 1048576)
224 finally:
225 + if p2cread is not None and p2cwrite is not None:
226 + _close_in_parent(p2cread)
227 + if c2pwrite is not None and c2pread is not None:
228 + _close_in_parent(c2pwrite)
229 + if errwrite is not None and errread is not None:
230 + _close_in_parent(errwrite)
231 +
232 # be sure the FD is closed no matter what
233 os.close(errpipe_read)
234
235 diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py
236 --- a/Lib/test/test_subprocess.py
237 +++ b/Lib/test/test_subprocess.py
238 @@ -14,6 +14,10 @@ try:
239 import resource
240 except ImportError:
241 resource = None
242 +try:
243 + import threading
244 +except ImportError:
245 + threading = None
246
247 mswindows = (sys.platform == "win32")
248
249 @@ -629,6 +633,36 @@ class ProcessTestCase(BaseTestCase):
250 if c.exception.errno not in (errno.ENOENT, errno.EACCES):
251 raise c.exception
252
253 + @unittest.skipIf(threading is None, "threading required")
254 + def test_double_close_on_error(self):
255 + # Issue #18851
256 + fds = []
257 + def open_fds():
258 + for i in range(20):
259 + fds.extend(os.pipe())
260 + time.sleep(0.001)
261 + t = threading.Thread(target=open_fds)
262 + t.start()
263 + try:
264 + with self.assertRaises(EnvironmentError):
265 + subprocess.Popen(['nonexisting_i_hope'],
266 + stdin=subprocess.PIPE,
267 + stdout=subprocess.PIPE,
268 + stderr=subprocess.PIPE)
269 + finally:
270 + t.join()
271 + exc = None
272 + for fd in fds:
273 + # If a double close occurred, some of those fds will
274 + # already have been closed by mistake, and os.close()
275 + # here will raise.
276 + try:
277 + os.close(fd)
278 + except OSError as e:
279 + exc = e
280 + if exc is not None:
281 + raise exc
282 +
283 def test_handles_closed_on_exception(self):
284 # If CreateProcess exits with an error, ensure the
285 # duplicate output handles are released
286 @@ -783,7 +817,7 @@ class POSIXProcessTestCase(BaseTestCase)
287
288 def _execute_child(
289 self, args, executable, preexec_fn, close_fds, cwd, env,
290 - universal_newlines, startupinfo, creationflags, shell,
291 + universal_newlines, startupinfo, creationflags, shell, to_close,
292 p2cread, p2cwrite,
293 c2pread, c2pwrite,
294 errread, errwrite):
295 @@ -791,7 +825,7 @@ class POSIXProcessTestCase(BaseTestCase)
296 subprocess.Popen._execute_child(
297 self, args, executable, preexec_fn, close_fds,
298 cwd, env, universal_newlines,
299 - startupinfo, creationflags, shell,
300 + startupinfo, creationflags, shell, to_close,
301 p2cread, p2cwrite,
302 c2pread, c2pwrite,
303 errread, errwrite)