/* * This function is code executed in the child process immediately after fork * to set things up and call exec(). * * All of the code in this function must only use async-signal-safe functions, * listed at `man 7 signal` or * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. * * This restriction is documented at * http://www.opengroup.org/onlinepubs/009695399/functions/fork.html. */ static void child_exec(char *const exec_array[], char *const argv[], char *const envp[], const char *cwd, int p2cread, int p2cwrite, int c2pread, int c2pwrite, int errread, int errwrite, int errpipe_read, int errpipe_write, int close_fds, int restore_signals, int call_setsid, PyObject *py_fds_to_keep, PyObject *preexec_fn, PyObject *preexec_fn_args_tuple) { int i, saved_errno, unused, reached_preexec = 0; PyObject *result; const char* err_msg = ""; /* Buffer large enough to hold a hex integer. We can't malloc. */ char hex_errno[sizeof(saved_errno)*2+1]; /* Close parent's pipe ends. */ if (p2cwrite != -1) { POSIX_CALL(close(p2cwrite)); } if (c2pread != -1) { POSIX_CALL(close(c2pread)); } if (errread != -1) { POSIX_CALL(close(errread)); } POSIX_CALL(close(errpipe_read)); /* When duping fds, if there arises a situation where one of the fds is either 0, 1 or 2, it is possible that it is overwritten (#12607). */ if (c2pwrite == 0) POSIX_CALL(c2pwrite = dup(c2pwrite)); if (errwrite == 0 || errwrite == 1) POSIX_CALL(errwrite = dup(errwrite)); /* Dup fds for child. dup2() removes the CLOEXEC flag but we must do it ourselves if dup2() would be a no-op (issue #10806). */ if (p2cread == 0) { int old = fcntl(p2cread, F_GETFD); if (old != -1) fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC); } else if (p2cread != -1) { POSIX_CALL(dup2(p2cread, 0)); /* stdin */ } if (c2pwrite == 1) { int old = fcntl(c2pwrite, F_GETFD); if (old != -1) fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC); } else if (c2pwrite != -1) { POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ } if (errwrite == 2) { int old = fcntl(errwrite, F_GETFD); if (old != -1) fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC); } else if (errwrite != -1) { POSIX_CALL(dup2(errwrite, 2)); /* stderr */ } /* Close pipe fds. Make sure we don't close the same fd more than */ /* once, or standard fds. */ if (p2cread > 2) { POSIX_CALL(close(p2cread)); } if (c2pwrite > 2 && c2pwrite != p2cread) { POSIX_CALL(close(c2pwrite)); } if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) { 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)); if (restore_signals) _Py_RestoreSignals(); #ifdef HAVE_SETSID if (call_setsid) POSIX_CALL(setsid()); #endif reached_preexec = 1; if (preexec_fn != Py_None && preexec_fn_args_tuple) { /* This is where the user has asked us to deadlock their program. */ result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL); if (result == NULL) { /* Stringifying the exception or traceback would involve * memory allocation and thus potential for deadlock. * We've already faced potential deadlock by calling back * into Python in the first place, so it probably doesn't * matter but we avoid it to minimize the possibility. */ err_msg = "Exception occurred in preexec_fn."; errno = 0; /* We don't want to report an OSError. */ goto error; } /* Py_DECREF(result); - We're about to exec so why bother? */ } /* This loop matches the Lib/os.py _execvpe()'s PATH search when */ /* given the executable_list generated by Lib/subprocess.py. */ saved_errno = 0; for (i = 0; exec_array[i] != NULL; ++i) { const char *executable = exec_array[i]; if (envp) { execve(executable, argv, envp); } else { execv(executable, argv); } if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) { saved_errno = errno; } } /* Report the first exec error, not the last. */ if (saved_errno) errno = saved_errno; error: saved_errno = errno; /* Report the posix error to our parent process. */ /* We ignore all write() return values as the total size of our writes is * less than PIPEBUF and we cannot do anything about an error anyways. */ if (saved_errno) { char *cur; unused = write(errpipe_write, "OSError:", 8); cur = hex_errno + sizeof(hex_errno); while (saved_errno != 0 && cur > hex_errno) { *--cur = "0123456789ABCDEF"[saved_errno % 16]; saved_errno /= 16; } unused = write(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur); unused = write(errpipe_write, ":", 1); if (!reached_preexec) { /* Indicate to the parent that the error happened before exec(). */ unused = write(errpipe_write, "noexec", 6); } /* We can't call strerror(saved_errno). It is not async signal safe. * The parent process will look the error message up. */ } else { unused = write(errpipe_write, "RuntimeError:0:", 15); unused = write(errpipe_write, err_msg, strlen(err_msg)); } if (unused) return; /* silly? yes! avoids gcc compiler warning. */ }
/* * This function is code executed in the child process immediately after fork * to set things up and call exec(). * * All of the code in this function must only use async-signal-safe functions, * listed at `man 7 signal` or * http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html. * * This restriction is documented at * http://www.opengroup.org/onlinepubs/009695399/functions/fork.html. */ static void child_exec(char *const exec_array[], char *const argv[], char *const envp[], const char *cwd, int p2cread, int p2cwrite, int c2pread, int c2pwrite, int errread, int errwrite, int errpipe_read, int errpipe_write, int close_fds, int restore_signals, int call_setsid, Py_ssize_t num_fds_to_keep, PyObject *py_fds_to_keep, PyObject *preexec_fn, PyObject *preexec_fn_args_tuple) { int i, saved_errno, fd_num; PyObject *result; const char* err_msg = ""; /* Buffer large enough to hold a hex integer. We can't malloc. */ char hex_errno[sizeof(saved_errno)*2+1]; /* Close parent's pipe ends. */ if (p2cwrite != -1) { POSIX_CALL(close(p2cwrite)); } if (c2pread != -1) { POSIX_CALL(close(c2pread)); } if (errread != -1) { POSIX_CALL(close(errread)); } POSIX_CALL(close(errpipe_read)); /* Dup fds for child. dup2() removes the CLOEXEC flag but we must do it ourselves if dup2() would be a no-op (issue #10806). */ if (p2cread == 0) { int old = fcntl(p2cread, F_GETFD); if (old != -1) fcntl(p2cread, F_SETFD, old & ~FD_CLOEXEC); } else if (p2cread != -1) { POSIX_CALL(dup2(p2cread, 0)); /* stdin */ } if (c2pwrite == 1) { int old = fcntl(c2pwrite, F_GETFD); if (old != -1) fcntl(c2pwrite, F_SETFD, old & ~FD_CLOEXEC); } else if (c2pwrite != -1) { POSIX_CALL(dup2(c2pwrite, 1)); /* stdout */ } if (errwrite == 2) { int old = fcntl(errwrite, F_GETFD); if (old != -1) fcntl(errwrite, F_SETFD, old & ~FD_CLOEXEC); } else if (errwrite != -1) { POSIX_CALL(dup2(errwrite, 2)); /* stderr */ } /* Close pipe fds. Make sure we don't close the same fd more than */ /* once, or standard fds. */ if (p2cread > 2) { POSIX_CALL(close(p2cread)); } if (c2pwrite > 2) { POSIX_CALL(close(c2pwrite)); } if (errwrite != c2pwrite && errwrite > 2) { POSIX_CALL(close(errwrite)); } /* close() is intentionally not checked for errors here as we are closing */ /* a large range of fds, some of which may be invalid. */ if (close_fds) { Py_ssize_t keep_seq_idx; int start_fd = 3; for (keep_seq_idx = 0; keep_seq_idx < num_fds_to_keep; ++keep_seq_idx) { PyObject* py_keep_fd = PySequence_Fast_GET_ITEM(py_fds_to_keep, keep_seq_idx); int keep_fd = PyLong_AsLong(py_keep_fd); if (keep_fd < 0) { /* Negative number, overflow or not a Long. */ err_msg = "bad value in fds_to_keep."; errno = 0; /* We don't want to report an OSError. */ goto error; } if (keep_fd < start_fd) continue; for (fd_num = start_fd; fd_num < keep_fd; ++fd_num) { close(fd_num); } start_fd = keep_fd + 1; } if (start_fd <= max_fd) { for (fd_num = start_fd; fd_num < max_fd; ++fd_num) { close(fd_num); } } } if (cwd) POSIX_CALL(chdir(cwd)); if (restore_signals) _Py_RestoreSignals(); #ifdef HAVE_SETSID if (call_setsid) POSIX_CALL(setsid()); #endif if (preexec_fn != Py_None && preexec_fn_args_tuple) { /* This is where the user has asked us to deadlock their program. */ result = PyObject_Call(preexec_fn, preexec_fn_args_tuple, NULL); if (result == NULL) { /* Stringifying the exception or traceback would involve * memory allocation and thus potential for deadlock. * We've already faced potential deadlock by calling back * into Python in the first place, so it probably doesn't * matter but we avoid it to minimize the possibility. */ err_msg = "Exception occurred in preexec_fn."; errno = 0; /* We don't want to report an OSError. */ goto error; } /* Py_DECREF(result); - We're about to exec so why bother? */ } /* This loop matches the Lib/os.py _execvpe()'s PATH search when */ /* given the executable_list generated by Lib/subprocess.py. */ saved_errno = 0; for (i = 0; exec_array[i] != NULL; ++i) { const char *executable = exec_array[i]; if (envp) { execve(executable, argv, envp); } else { execv(executable, argv); } if (errno != ENOENT && errno != ENOTDIR && saved_errno == 0) { saved_errno = errno; } } /* Report the first exec error, not the last. */ if (saved_errno) errno = saved_errno; error: saved_errno = errno; /* Report the posix error to our parent process. */ if (saved_errno) { char *cur; write(errpipe_write, "OSError:", 8); cur = hex_errno + sizeof(hex_errno); while (saved_errno != 0 && cur > hex_errno) { *--cur = "0123456789ABCDEF"[saved_errno % 16]; saved_errno /= 16; } write(errpipe_write, cur, hex_errno + sizeof(hex_errno) - cur); write(errpipe_write, ":", 1); /* We can't call strerror(saved_errno). It is not async signal safe. * The parent process will look the error message up. */ } else { write(errpipe_write, "RuntimeError:0:", 15); write(errpipe_write, err_msg, strlen(err_msg)); } }