Skip to content

Commit 1eda9e7

Browse files
committed
Fixes Issue python#15798 - subprocess.Popen() no longer fails if file
descriptor 0, 1 or 2 is closed.
1 parent 65846c6 commit 1eda9e7

4 files changed

Lines changed: 29 additions & 6 deletions

File tree

‎Lib/subprocess.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,10 @@ def _execute_child(self, args, executable, preexec_fn, close_fds,
13681368
executable_list = tuple(
13691369
os.path.join(os.fsencode(dir), executable)
13701370
for dir in os.get_exec_path(env))
1371-
fds_to_keep = set(pass_fds)
1371+
# Never close stdin, stdout and stderr for the child.
1372+
fds_to_keep = {0,1,2}
1373+
fds_to_keep.update(pass_fds)
1374+
# Our child uses this one to signal error before exec().
13721375
fds_to_keep.add(errpipe_write)
13731376
self.pid = _posixsubprocess.fork_exec(
13741377
args, executable_list,

‎Lib/test/test_subprocess.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,27 @@ def test_close_fds_0_1_2(self):
15591559
# all standard fds closed.
15601560
self.check_close_std_fds([0, 1, 2])
15611561

1562+
def test_small_errpipe_write_fd(self):
1563+
"""Issue #15798: Popen should work when stdio fds are available."""
1564+
new_stdin = os.dup(0)
1565+
new_stdout = os.dup(1)
1566+
try:
1567+
os.close(0)
1568+
os.close(1)
1569+
1570+
# Side test: if errpipe_write fails to have its CLOEXEC
1571+
# flag set this should cause the parent to think the exec
1572+
# failed. Extremely unlikely: everyone supports CLOEXEC.
1573+
subprocess.Popen([
1574+
sys.executable, "-c",
1575+
"print('AssertionError:0:CLOEXEC failure.')"]).wait()
1576+
finally:
1577+
# Restore original stdin and stdout
1578+
os.dup2(new_stdin, 0)
1579+
os.dup2(new_stdout, 1)
1580+
os.close(new_stdin)
1581+
os.close(new_stdout)
1582+
15621583
def test_remapping_std_fds(self):
15631584
# open up some temporary files
15641585
temps = [mkstemp() for i in range(3)]

‎Misc/NEWS‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Core and Builtins
1818
Library
1919
-------
2020

21+
- Issue #15798: Fixed subprocess.Popen() to no longer fail if file
22+
descriptor 0, 1 or 2 is closed.
23+
2124
- Issue #19088: Fixed incorrect caching of the copyreg module in
2225
object.__reduce__() and object.__reduce_ex__().
2326

‎Modules/_posixsubprocess.c‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ child_exec(char *const exec_array[],
449449
local_max_fd = max_fd;
450450
#endif
451451
/* TODO HP-UX could use pstat_getproc() if anyone cares about it. */
452-
_close_open_fd_range(3, local_max_fd, py_fds_to_keep);
452+
_close_open_fd_range(0, local_max_fd, py_fds_to_keep);
453453
}
454454

455455
/* This loop matches the Lib/os.py _execvpe()'s PATH search when */
@@ -526,10 +526,6 @@ subprocess_fork_exec(PyObject* self, PyObject *args)
526526
&restore_signals, &call_setsid, &preexec_fn))
527527
return NULL;
528528

529-
if (close_fds && errpipe_write < 3) { /* precondition */
530-
PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3");
531-
return NULL;
532-
}
533529
if (PySequence_Length(py_fds_to_keep) < 0) {
534530
PyErr_SetString(PyExc_ValueError, "cannot get length of fds_to_keep");
535531
return NULL;

0 commit comments

Comments
 (0)