diff --git a/cbits/posix/bsd_closefrom.c b/cbits/posix/bsd_closefrom.c new file mode 100644 index 00000000..301fdb36 --- /dev/null +++ b/cbits/posix/bsd_closefrom.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2004-2005 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if !defined(HAVE_CLOSEFROM) || defined(BROKEN_CLOSEFROM) + +#include +#include +#include +#ifdef HAVE_FCNTL_H +# include +#endif +#include +#include +#include +#include +#include +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif +#if defined(HAVE_LIBPROC_H) +# include +#endif + +#ifndef OPEN_MAX +# define OPEN_MAX 256 +#endif + +#if 0 +__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; +#endif /* lint */ + +#ifndef HAVE_FCNTL_CLOSEM +/* + * Close all file descriptors greater than or equal to lowfd. + */ +static void +hs_process_closefrom_fallback(int lowfd) +{ + long fd, maxfd; + + /* + * Fall back on sysconf() or getdtablesize(). We avoid checking + * resource limits since it is possible to open a file descriptor + * and then drop the rlimit such that it is below the open fd. + */ +#ifdef HAVE_SYSCONF + maxfd = sysconf(_SC_OPEN_MAX); +#else + maxfd = getdtablesize(); +#endif /* HAVE_SYSCONF */ + if (maxfd < 0) + maxfd = OPEN_MAX; + + for (fd = lowfd; fd < maxfd; fd++) + (void) close((int) fd); +} +#endif /* HAVE_FCNTL_CLOSEM */ + +#ifdef HAVE_FCNTL_CLOSEM +void +hs_process_closefrom(int lowfd) +{ + (void) fcntl(lowfd, F_CLOSEM, 0); +} +#elif defined(HAVE_LIBPROC_H) && defined(HAVE_PROC_PIDINFO) +void +hs_process_closefrom(int lowfd) +{ + int i, r, sz; + pid_t pid = getpid(); + struct proc_fdinfo *fdinfo_buf = NULL; + + sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (sz == 0) + return; /* no fds, really? */ + else if (sz == -1) + goto fallback; + if ((fdinfo_buf = malloc(sz)) == NULL) + goto fallback; + r = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdinfo_buf, sz); + if (r < 0 || r > sz) + goto fallback; + for (i = 0; i < r / (int)PROC_PIDLISTFD_SIZE; i++) { + if (fdinfo_buf[i].proc_fd >= lowfd) + close(fdinfo_buf[i].proc_fd); + } + free(fdinfo_buf); + return; + fallback: + free(fdinfo_buf); + hs_process_closefrom_fallback(lowfd); + return; +} +#elif defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) +void +hs_process_closefrom(int lowfd) +{ + long fd; + char fdpath[PATH_MAX], *endp; + struct dirent *dent; + DIR *dirp; + int len; + +#ifdef HAVE_CLOSE_RANGE + if (close_range(lowfd, INT_MAX, 0) == 0) + return; +#endif + + /* Check for a /proc/$$/fd directory. */ + len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); + if (len > 0 && (size_t)len < sizeof(fdpath) && (dirp = opendir(fdpath))) { + while ((dent = readdir(dirp)) != NULL) { + fd = strtol(dent->d_name, &endp, 10); + if (dent->d_name != endp && *endp == '\0' && + fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) + (void) close((int) fd); + } + (void) closedir(dirp); + return; + } + /* /proc/$$/fd strategy failed, fall back to brute force closure */ + hs_process_closefrom_fallback(lowfd); +} +#else +void +hs_process_closefrom(int lowfd) +{ + hs_process_closefrom_fallback(lowfd); +} +#endif /* !HAVE_FCNTL_CLOSEM */ +#endif /* HAVE_CLOSEFROM */ diff --git a/cbits/posix/bsd_closefrom.h b/cbits/posix/bsd_closefrom.h new file mode 100644 index 00000000..db7eb427 --- /dev/null +++ b/cbits/posix/bsd_closefrom.h @@ -0,0 +1,6 @@ +#ifndef BSD_CLOSEFROM_H_ +#define BSD_CLOSEFROM_H_ + +void hs_process_closefrom(int lowfd); + +#endif // BSD_CLOSEFROM_H_ diff --git a/cbits/posix/common.h b/cbits/posix/common.h index d7693ad8..e0063eb9 100644 --- a/cbits/posix/common.h +++ b/cbits/posix/common.h @@ -22,7 +22,7 @@ struct std_handle { }; }; -int get_max_fd(void); +void hs_process_closefrom_excluding(int from, int excluding); // defined in find_executable.c #if !defined(HAVE_EXECVPE) diff --git a/cbits/posix/fork_exec.c b/cbits/posix/fork_exec.c index dd6e58f1..b35e9b13 100644 --- a/cbits/posix/fork_exec.c +++ b/cbits/posix/fork_exec.c @@ -222,13 +222,7 @@ do_spawn_fork (char *const args[], setup_std_handle_fork(STDERR_FILENO, stdErrHdl, forkCommunicationFds[1]); if ((flags & RUN_PROCESS_IN_CLOSE_FDS) != 0) { - int max_fd = get_max_fd(); - // XXX Not the pipe - for (int i = 3; i < max_fd; i++) { - if (i != forkCommunicationFds[1]) { - close(i); - } - } + hs_process_closefrom_excluding(3, forkCommunicationFds[1]); } /* Reset the SIGINT/SIGQUIT signal handlers in the child, if requested diff --git a/cbits/posix/runProcess.c b/cbits/posix/runProcess.c index bc350d97..a4e40e5a 100644 --- a/cbits/posix/runProcess.c +++ b/cbits/posix/runProcess.c @@ -9,8 +9,11 @@ #if defined(HAVE_FORK) +#include "bsd_closefrom.h" + #include #include +#include #include #ifdef HAVE_FCNTL_H @@ -21,21 +24,27 @@ #include #endif -int -get_max_fd() -{ - static int cache = 0; - if (cache == 0) { -#if HAVE_SYSCONF - cache = sysconf(_SC_OPEN_MAX); - if (cache == -1) { - cache = 256; - } +void +hs_process_closefrom_excluding(int lowfd, int excludingFd) { +#ifdef SYS_close_range + // Try using the close_range syscall, provided in Linux kernel >= 5.9. + // We do this directly because not all C libs provide a wrapper (like musl) + long ret = syscall(SYS_close_range, lowfd, excludingFd - 1, 0); #else - cache = 256; + long ret = -1; #endif + + if (ret != -1) { + // If that worked, closefrom the remaining range + hs_process_closefrom(excludingFd + 1); + } else { + // Otherwise, fall back to a loop + closefrom + for (int i = lowfd; i < excludingFd; i++) { + close(i); + } + + hs_process_closefrom(excludingFd + 1); } - return cache; } // If a process was terminated by a signal, the exit status we return diff --git a/configure.ac b/configure.ac index 367f14c1..63f49e7d 100644 --- a/configure.ac +++ b/configure.ac @@ -16,6 +16,18 @@ AC_CHECK_HEADERS([signal.h sys/wait.h fcntl.h]) AC_CHECK_FUNCS([setitimer sysconf]) AC_CHECK_FUNCS([execvpe]) +# check for headers and functions used by bsd_closefrom.c +AC_CHECK_HEADERS([dirent.h fcntl.h libproc.h linux/close_range.h ndir.h sys/dir.h sys/ndir.h]) +AC_CHECK_FUNCS([closefrom close_range dirfd proc_pidinfo sysconf]) +AC_MSG_CHECKING([for /proc/pid/fd directory]) +if test -d "/proc/$$/fd" ; then + AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + + # posix_spawn checks AC_CHECK_HEADERS([spawn.h]) AC_CHECK_FUNCS([posix_spawnp posix_spawn_file_actions_addchdir],[],[],[ diff --git a/process.cabal b/process.cabal index fe7564e1..8aff4a98 100644 --- a/process.cabal +++ b/process.cabal @@ -57,6 +57,10 @@ library if os(windows) c-sources: cbits/win32/runProcess.c + include-dirs: + include + includes: + runProcess.h other-modules: System.Process.Windows build-depends: Win32 >=2.4 && < 2.14 -- ole32 and rpcrt4 are needed to create GUIDs for unique named pipes @@ -66,15 +70,19 @@ library else c-sources: cbits/posix/runProcess.c + cbits/posix/bsd_closefrom.c cbits/posix/fork_exec.c cbits/posix/posix_spawn.c cbits/posix/find_executable.c + include-dirs: + include + cbits/posix + includes: + runProcess.h + bsd_closefrom.h other-modules: System.Process.Posix build-depends: unix >= 2.5 && < 2.9 - include-dirs: include - includes: - runProcess.h install-includes: runProcess.h processFlags.h