diff --git a/config.h.in b/config.h.in index 428b8a342..d97c7ecce 100644 --- a/config.h.in +++ b/config.h.in @@ -539,6 +539,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LIBINTL_H +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBPROC_H + /* Define to 1 if you have the header file. */ #undef HAVE_LIBUTIL_H @@ -674,6 +677,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_PROCFS_H +/* Define to 1 if you have the `proc_pidinfo' function. */ +#undef HAVE_PROC_PIDINFO + /* Define to 1 if you have the header file. */ #undef HAVE_PROJECT_H diff --git a/configure b/configure index 84223dac1..68228fca3 100755 --- a/configure +++ b/configure @@ -17851,6 +17851,24 @@ printf "%s\n" "#define HAVE_DECL_GETGROUPLIST_2 $ac_have_decl" >>confdefs.h fi +done + + # We use proc_pidinfo() to emulate closefrom() on macOS. + for ac_header in libproc.h +do : + ac_fn_c_check_header_compile "$LINENO" "libproc.h" "ac_cv_header_libproc_h" "$ac_includes_default" +if test "x$ac_cv_header_libproc_h" = xyes +then : + printf "%s\n" "#define HAVE_LIBPROC_H 1" >>confdefs.h + ac_fn_c_check_func "$LINENO" "proc_pidinfo" "ac_cv_func_proc_pidinfo" +if test "x$ac_cv_func_proc_pidinfo" = xyes +then : + printf "%s\n" "#define HAVE_PROC_PIDINFO 1" >>confdefs.h + +fi + +fi + done # We need to force a flat namespace to make libc diff --git a/configure.ac b/configure.ac index bcd615c54..b58f142b5 100644 --- a/configure.ac +++ b/configure.ac @@ -2205,6 +2205,9 @@ case "$host" in # Undocumented API that dynamically allocates the groups. AC_CHECK_FUNCS([getgrouplist_2], [AC_CHECK_DECLS([getgrouplist_2])]) + # We use proc_pidinfo() to emulate closefrom() on macOS. + AC_CHECK_HEADERS([libproc.h], [AC_CHECK_FUNCS([proc_pidinfo])]) + # We need to force a flat namespace to make libc # symbol hooking work like it does on ELF. AX_CHECK_LINK_FLAG([-Wl,-force_flat_namespace], [AX_APPEND_FLAG([-Wl,-force_flat_namespace], [SUDO_LDFLAGS])]) diff --git a/lib/util/closefrom.c b/lib/util/closefrom.c index 1b212aa7f..8608e68c5 100644 --- a/lib/util/closefrom.c +++ b/lib/util/closefrom.c @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: ISC * - * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2018 + * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2021 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -29,12 +29,16 @@ #include #include #include +#include #include #ifdef HAVE_PSTAT_GETPROC # include #else # include #endif +#ifdef HAVE_LIBPROC_H +# include +#endif #include "sudo_compat.h" #include "sudo_util.h" @@ -44,6 +48,13 @@ # define OPEN_MAX 256 #endif +/* Avoid potential libdispatch crash on macOS when we close its fds. */ +#ifdef __APPLE__ +# define closefrom_close(x) fcntl((x), F_SETFD, FD_CLOEXEC) +#else +# define closefrom_close(x) close(x) +#endif + /* * Close all file descriptors greater than or equal to lowfd. * This is the expensive (fallback) method. @@ -69,12 +80,7 @@ closefrom_fallback(int lowfd) maxfd = INT_MAX; for (fd = lowfd; fd < maxfd; fd++) { -#ifdef __APPLE__ - /* Avoid potential libdispatch crash when we close its fds. */ - (void) fcntl((int) fd, F_SETFD, FD_CLOEXEC); -#else - (void) close((int) fd); -#endif + (void)closefrom_close((int)fd); } } @@ -91,12 +97,45 @@ sudo_closefrom(int lowfd) const char *path; DIR *dirp; #endif +#if defined(HAVE_PROC_PIDINFO) + struct proc_fdinfo *buf = NULL; + const pid_t pid = getpid(); + int i, n, len; +#endif /* Try the fast method first, if possible. */ #if defined(HAVE_FCNTL_CLOSEM) if (fcntl(lowfd, F_CLOSEM, 0) != -1) return; -#endif +#elif defined(HAVE_PROC_PIDINFO) + len = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + switch (len) { + case 0: + /* No open files. */ + return; + case -1: + /* Fall back on other methods. */ + break; + default: + /* Allocate space for 4 extra fds to leave some wiggle room. */ + buf = malloc(len + (PROC_PIDLISTFD_SIZE * 4)); + if (buf == NULL) + break; + n = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, buf, len); + if (n == -1 || n > len) { + free(buf); + break; + } + n /= PROC_PIDLISTFD_SIZE; + for (i = 0; i < n; i++) { + if (buf[i].proc_fd >= lowfd) { + (void)closefrom_close(buf[i].proc_fd); + } + } + free(buf); + return; + } +#endif /* HAVE_PROC_PIDINFO */ #if defined(HAVE_PSTAT_GETPROC) /* * EOVERFLOW is not a fatal error for the fields we use. @@ -107,7 +146,7 @@ sudo_closefrom(int lowfd) int fd; for (fd = lowfd; fd <= pst.pst_highestfd; fd++) - (void) close(fd); + (void)closefrom_close(fd); return; } #elif defined(HAVE_DIRFD) @@ -123,15 +162,10 @@ sudo_closefrom(int lowfd) const char *errstr; int fd = sudo_strtonum(dent->d_name, lowfd, INT_MAX, &errstr); if (errstr == NULL && fd != dirfd(dirp)) { -# ifdef __APPLE__ - /* Avoid potential libdispatch crash when we close its fds. */ - (void) fcntl(fd, F_SETFD, FD_CLOEXEC); -# else - (void) close(fd); -# endif + (void)closefrom_close(fd); } } - (void) closedir(dirp); + (void)closedir(dirp); return; } #endif /* HAVE_DIRFD */