Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 249cdc3

Browse files
committed
Issue #18763: subprocess: The file descriptors are now closed after calling the
preexec_fn callback, which may open file descriptors.
1 parent ec8147b commit 249cdc3

2 files changed

Lines changed: 29 additions & 11 deletions

File tree

Lib/test/test_subprocess.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,23 @@ def test_leak_fast_process_del_killed(self):
19381938
self.assertRaises(OSError, os.waitpid, pid, 0)
19391939
self.assertNotIn(ident, [id(o) for o in subprocess._active])
19401940

1941+
def test_close_fds_after_preexec(self):
1942+
fd_status = support.findfile("fd_status.py", subdir="subprocessdata")
1943+
1944+
# this FD is used as dup2() target by preexec_fn, and should be closed
1945+
# in the child process
1946+
fd = os.dup(1)
1947+
self.addCleanup(os.close, fd)
1948+
1949+
p = subprocess.Popen([sys.executable, fd_status],
1950+
stdout=subprocess.PIPE, close_fds=True,
1951+
preexec_fn=lambda: os.dup2(1, fd))
1952+
output, ignored = p.communicate()
1953+
1954+
remaining_fds = set(map(int, output.split(b',')))
1955+
1956+
self.assertNotIn(fd, remaining_fds)
1957+
19411958

19421959
@unittest.skipUnless(mswindows, "Windows specific tests")
19431960
class Win32ProcessTestCase(BaseTestCase):

Modules/_posixsubprocess.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -412,17 +412,6 @@ child_exec(char *const exec_array[],
412412
POSIX_CALL(close(errwrite));
413413
}
414414

415-
if (close_fds) {
416-
int local_max_fd = max_fd;
417-
#if defined(__NetBSD__)
418-
local_max_fd = fcntl(0, F_MAXFD);
419-
if (local_max_fd < 0)
420-
local_max_fd = max_fd;
421-
#endif
422-
/* TODO HP-UX could use pstat_getproc() if anyone cares about it. */
423-
_close_open_fd_range(3, local_max_fd, py_fds_to_keep);
424-
}
425-
426415
if (cwd)
427416
POSIX_CALL(chdir(cwd));
428417

@@ -451,6 +440,18 @@ child_exec(char *const exec_array[],
451440
/* Py_DECREF(result); - We're about to exec so why bother? */
452441
}
453442

443+
/* close FDs after executing preexec_fn, which might open FDs */
444+
if (close_fds) {
445+
int local_max_fd = max_fd;
446+
#if defined(__NetBSD__)
447+
local_max_fd = fcntl(0, F_MAXFD);
448+
if (local_max_fd < 0)
449+
local_max_fd = max_fd;
450+
#endif
451+
/* 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);
453+
}
454+
454455
/* This loop matches the Lib/os.py _execvpe()'s PATH search when */
455456
/* given the executable_list generated by Lib/subprocess.py. */
456457
saved_errno = 0;

0 commit comments

Comments
 (0)