diff options
Diffstat (limited to 'Modules/_posixsubprocess.c')
| -rw-r--r-- | Modules/_posixsubprocess.c | 115 |
1 files changed, 74 insertions, 41 deletions
diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index 19ca31f..0196ca2 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -47,7 +47,9 @@ static int _enable_gc(PyObject *gc_module) { PyObject *result; - result = PyObject_CallMethod(gc_module, "enable", NULL); + _Py_IDENTIFIER(enable); + + result = _PyObject_CallMethodId(gc_module, &PyId_enable, NULL); if (result == NULL) return 1; Py_DECREF(result); @@ -78,7 +80,7 @@ _pos_int_from_ascii(char *name) * that properly supports /dev/fd. */ static int -_is_fdescfs_mounted_on_dev_fd() +_is_fdescfs_mounted_on_dev_fd(void) { struct stat dev_stat; struct stat dev_fd_stat; @@ -174,17 +176,11 @@ _close_fds_by_brute_force(int start_fd, int end_fd, PyObject *py_fds_to_keep) * This structure is very old and stable: It will not change unless the kernel * chooses to break compatibility with all existing binaries. Highly Unlikely. */ -struct linux_dirent { -#if defined(__x86_64__) && defined(__ILP32__) - /* Support the wacky x32 ABI (fake 32-bit userspace speaking to x86_64 - * kernel interfaces) - https://sites.google.com/site/x32abi/ */ +struct linux_dirent64 { unsigned long long d_ino; - unsigned long long d_off; -#else - unsigned long d_ino; /* Inode number */ - unsigned long d_off; /* Offset to next linux_dirent */ -#endif + long long d_off; unsigned short d_reclen; /* Length of this linux_dirent */ + unsigned char d_type; char d_name[256]; /* Filename (null-terminated) */ }; @@ -226,16 +222,16 @@ _close_open_fd_range_safe(int start_fd, int end_fd, PyObject* py_fds_to_keep) _close_fds_by_brute_force(start_fd, end_fd, py_fds_to_keep); return; } else { - char buffer[sizeof(struct linux_dirent)]; + char buffer[sizeof(struct linux_dirent64)]; int bytes; - while ((bytes = syscall(SYS_getdents, fd_dir_fd, - (struct linux_dirent *)buffer, + while ((bytes = syscall(SYS_getdents64, fd_dir_fd, + (struct linux_dirent64 *)buffer, sizeof(buffer))) > 0) { - struct linux_dirent *entry; + struct linux_dirent64 *entry; int offset; for (offset = 0; offset < bytes; offset += entry->d_reclen) { int fd; - entry = (struct linux_dirent *)(buffer + offset); + entry = (struct linux_dirent64 *)(buffer + offset); if ((fd = _pos_int_from_ascii(entry->d_name)) < 0) continue; /* Not a number. */ if (fd != fd_dir_fd && fd >= start_fd && fd < end_fd && @@ -416,17 +412,6 @@ child_exec(char *const exec_array[], POSIX_CALL(close(errwrite)); } - if (close_fds) { - int local_max_fd = max_fd; -#if defined(__NetBSD__) - local_max_fd = fcntl(0, F_MAXFD); - if (local_max_fd < 0) - local_max_fd = max_fd; -#endif - /* TODO HP-UX could use pstat_getproc() if anyone cares about it. */ - _close_open_fd_range(3, local_max_fd, py_fds_to_keep); - } - if (cwd) POSIX_CALL(chdir(cwd)); @@ -455,6 +440,18 @@ child_exec(char *const exec_array[], /* Py_DECREF(result); - We're about to exec so why bother? */ } + /* close FDs after executing preexec_fn, which might open FDs */ + if (close_fds) { + int local_max_fd = max_fd; +#if defined(__NetBSD__) + local_max_fd = fcntl(0, F_MAXFD); + if (local_max_fd < 0) + local_max_fd = max_fd; +#endif + /* TODO HP-UX could use pstat_getproc() if anyone cares about it. */ + _close_open_fd_range(3, local_max_fd, py_fds_to_keep); + } + /* This loop matches the Lib/os.py _execvpe()'s PATH search when */ /* given the executable_list generated by Lib/subprocess.py. */ saved_errno = 0; @@ -506,7 +503,7 @@ static PyObject * subprocess_fork_exec(PyObject* self, PyObject *args) { PyObject *gc_module = NULL; - PyObject *executable_list, *py_close_fds, *py_fds_to_keep; + PyObject *executable_list, *py_fds_to_keep; PyObject *env_list, *preexec_fn; PyObject *process_args, *converted_args = NULL, *fast_args = NULL; PyObject *preexec_fn_args_tuple = NULL; @@ -521,17 +518,14 @@ subprocess_fork_exec(PyObject* self, PyObject *args) Py_ssize_t arg_num; if (!PyArg_ParseTuple( - args, "OOOOOOiiiiiiiiiiO:fork_exec", - &process_args, &executable_list, &py_close_fds, &py_fds_to_keep, + args, "OOpOOOiiiiiiiiiiO:fork_exec", + &process_args, &executable_list, &close_fds, &py_fds_to_keep, &cwd_obj, &env_list, &p2cread, &p2cwrite, &c2pread, &c2pwrite, &errread, &errwrite, &errpipe_read, &errpipe_write, &restore_signals, &call_setsid, &preexec_fn)) return NULL; - close_fds = PyObject_IsTrue(py_close_fds); - if (close_fds < 0) - return NULL; if (close_fds && errpipe_write < 3) { /* precondition */ PyErr_SetString(PyExc_ValueError, "errpipe_write must be >= 3"); return NULL; @@ -548,10 +542,13 @@ subprocess_fork_exec(PyObject* self, PyObject *args) /* We need to call gc.disable() when we'll be calling preexec_fn */ if (preexec_fn != Py_None) { PyObject *result; + _Py_IDENTIFIER(isenabled); + _Py_IDENTIFIER(disable); + gc_module = PyImport_ImportModule("gc"); if (gc_module == NULL) return NULL; - result = PyObject_CallMethod(gc_module, "isenabled", NULL); + result = _PyObject_CallMethodId(gc_module, &PyId_isenabled, NULL); if (result == NULL) { Py_DECREF(gc_module); return NULL; @@ -562,7 +559,7 @@ subprocess_fork_exec(PyObject* self, PyObject *args) Py_DECREF(gc_module); return NULL; } - result = PyObject_CallMethod(gc_module, "disable", NULL); + result = _PyObject_CallMethodId(gc_module, &PyId_disable, NULL); if (result == NULL) { Py_DECREF(gc_module); return NULL; @@ -726,26 +723,24 @@ Raises: Only on an error in the parent process.\n\ PyDoc_STRVAR(subprocess_cloexec_pipe_doc, "cloexec_pipe() -> (read_end, write_end)\n\n\ -Create a pipe whose ends have the cloexec flag set."); +Create a pipe whose ends have the cloexec flag set; write_end will be >= 3."); static PyObject * subprocess_cloexec_pipe(PyObject *self, PyObject *noargs) { int fds[2]; - int res; + int res, saved_errno; + long oldflags; #ifdef HAVE_PIPE2 Py_BEGIN_ALLOW_THREADS res = pipe2(fds, O_CLOEXEC); Py_END_ALLOW_THREADS if (res != 0 && errno == ENOSYS) { - { #endif /* We hold the GIL which offers some protection from other code calling * fork() before the CLOEXEC flags have been set but we can't guarantee * anything without pipe2(). */ - long oldflags; - res = pipe(fds); if (res == 0) { @@ -762,9 +757,47 @@ subprocess_cloexec_pipe(PyObject *self, PyObject *noargs) if (res == 0) res = fcntl(fds[1], F_SETFD, oldflags | FD_CLOEXEC); #ifdef HAVE_PIPE2 - } } #endif + if (res == 0 && fds[1] < 3) { + /* We always want the write end of the pipe to avoid fds 0, 1 and 2 + * as our child may claim those for stdio connections. */ + int write_fd = fds[1]; + int fds_to_close[3] = {-1, -1, -1}; + int fds_to_close_idx = 0; +#ifdef F_DUPFD_CLOEXEC + fds_to_close[fds_to_close_idx++] = write_fd; + write_fd = fcntl(write_fd, F_DUPFD_CLOEXEC, 3); + if (write_fd < 0) /* We don't support F_DUPFD_CLOEXEC / other error */ +#endif + { + /* Use dup a few times until we get a desirable fd. */ + for (; fds_to_close_idx < 3; ++fds_to_close_idx) { + fds_to_close[fds_to_close_idx] = write_fd; + write_fd = dup(write_fd); + if (write_fd >= 3) + break; + /* We may dup a few extra times if it returns an error but + * that is okay. Repeat calls should return the same error. */ + } + if (write_fd < 0) res = write_fd; + if (res == 0) { + oldflags = fcntl(write_fd, F_GETFD, 0); + if (oldflags < 0) res = oldflags; + if (res == 0) + res = fcntl(write_fd, F_SETFD, oldflags | FD_CLOEXEC); + } + } + saved_errno = errno; + /* Close fds we tried for the write end that were too low. */ + for (fds_to_close_idx=0; fds_to_close_idx < 3; ++fds_to_close_idx) { + int temp_fd = fds_to_close[fds_to_close_idx]; + while (temp_fd >= 0 && close(temp_fd) < 0 && errno == EINTR); + } + errno = saved_errno; /* report dup or fcntl errors, not close. */ + fds[1] = write_fd; + } /* end if write fd was too small */ + if (res != 0) return PyErr_SetFromErrno(PyExc_OSError); return Py_BuildValue("(ii)", fds[0], fds[1]); |
