From 45f4acd1523f02695400e16a70733c0cf49bbcc1 Mon Sep 17 00:00:00 2001 From: dockes Date: Wed, 14 Oct 2009 12:22:21 +0000 Subject: [PATCH] --- src/utils/closefrom.cpp | 218 ++++++++++++++++++++++++++++++++++++++++ src/utils/closefrom.h | 8 ++ 2 files changed, 226 insertions(+) create mode 100644 src/utils/closefrom.cpp create mode 100644 src/utils/closefrom.h diff --git a/src/utils/closefrom.cpp b/src/utils/closefrom.cpp new file mode 100644 index 00000000..77e48e10 --- /dev/null +++ b/src/utils/closefrom.cpp @@ -0,0 +1,218 @@ +#ifndef lint +static char rcsid[] = "@(#$Id: $ (C) 2009 J.F.Dockes"; +#endif +/* + * Close all file descriptors above a given value. + * + * A Unix execXX() call used to execute another program does not close open + * file descriptors by default. + * + * The only descriptors closed are those on which the FD_CLOEXEC flag was + * set. FD_CLOEXEC is not easily usable on files opened by external + * libraries. + * + * There are many reasons for closing file descriptors before + * an exec (security, pipe control, the possibility that a bug will trigger + * an unwanted write, etc.) + * + * A process has currently no POSIX way to determine the list of open file + * descriptors or at least the highest value. Closing all files (except a few), + * thus implies performing a close() system call on each entry up to the + * maximum, which can be both relatively difficult to determine, and quite + * high (ie: several thousands), incurring a non-negligible cost. + * + * A number of systems have non-portable support for mitigating or solving + * this problem. + * + * This module supplies a portable interface to this functionality. + * + * The initial data on system interfaces was obtained from: + * http://stackoverflow.com/questions/899038/\ + * getting-the-highest-allocated-file-descriptor + * + * System interfaces: + * FreeBSD: + * - Has a closefrom() system call as of release 7.x around Sep 2009 + * - Has a /dev/fd, directory which lists the current process' open + * descriptors. Only descriptors 0, 1, 2 are shown except if + * fdescfs is mounted which it is not by default + * + * Interface: + * int libclf_closefrom(fd) + * @param fd All open file descriptors with equal or higher numeric + * values will be closed. fd needs not be a valid descriptor. + * @return 0 for success, -1 for error. + */ +#ifndef TEST_CLOSEFROM +#include +#include +#include +#include + +/* #define DEBUG_CLOSEFROM*/ +#ifdef DEBUG_CLOSEFROM +#define DPRINT(X) fprintf X +#else +#define DPRINT(X) +#endif + +/* Note: sudo has a closefrom implementation, needs a lot of autoconfig, but + * we could use it instead. It's quite close to this though */ + +/*************************************************************************/ + +/* closefrom() exists on Solaris, netbsd and openbsd, but someone will + * have to provide me the appropriate macro to test */ +#if (defined(__FreeBSD__) && __FreeBSD_version >= 702104) +/* Use closefrom() system call */ +int libclf_closefrom(int fd0) +{ + DPRINT((stderr, "libclf_closefrom: using closefrom(2)\n")); + closefrom(fd0); + return 0; +} + +/*************************************************************************/ +#elif defined(F_CLOSEM) + +/* Use fcntl(fd, F_CLOSEM) */ + +int libclf_closefrom(int fd0) +{ + DPRINT((stderr, "libclf_closefrom: using fcntl(F_CLOSEM)\n")); + // We need a valid descriptor for this to work. Try to dup stdin, else + // go wild + if (fcntl(0, F_GETFL) != -1) { + if (fd0 != 0) + dup2(0, fd0); + } else { + int fd = open("/etc/group", 0); // yes i am a unix man + if (fd >= 0 && fd != fd0) { + dup2(fd, fd0); + close(fd); + } + } + return fcntl(fd0, F_CLOSEM, 0); +} + +/*************************************************************************/ +#elif defined(linux) + +/* Use /proc/self/fd directory */ +#include +#include + +int libclf_closefrom(int fd0) +{ + DIR *dirp; + struct dirent *ent; + + DPRINT((stderr, "libclf_closefrom: using /proc\n")); + dirp = opendir("/proc/self/fd"); + if (dirp == 0) + return -1; + + while ((ent = readdir(dirp)) != 0) { + int fd; + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + + if (sscanf(ent->d_name, "%d", &fd) == 1 && fd >= fd0 && + fd != dirfd(dirp)) { + close(fd); + } + } + closedir(dirp); + return 0; +} + +/*************************************************************************/ +#else +/* System has no native support for this functionality whatsoever. + * + * Close all descriptors up to compiled/configured maximum. + * The caller will usually have an idea of a reasonable maximum, else + * we retrieve a value from the system. + */ + +static int closefrom_maxfd = -1; + +void libclf_setmaxfd(int max) +{ + closefrom_maxfd = max; +} + +int libclf_closefrom(int fd0) +{ + int i, maxfd = closefrom_maxfd; + + if (maxfd < 0) { +#ifdef _SC_OPEN_MAX + maxfd = sysconf(_SC_OPEN_MAX); + DPRINT((stderr, "Maxfd is %d after sysconf()\n", maxfd)); +#else + maxfd = getdtablesize(); + DPRINT((stderr, "Maxfd is %d after getdtablesize()\n", maxfd)); +#endif + } + if (maxfd < 0) + maxfd = OPEN_MAX; + + DPRINT((stderr, "libclf_closefrom: using loop to %d\n", maxfd)); + + for (i = fd0; i < maxfd; i++) { + (void)close(i); + } + return 0; +} +#endif + + +#else /* TEST_CLOSEFROM */ + +#include +#include +#include +#include + +#include "closefrom.h" + +int main(int argc, char **argv) +{ + int i; + + int fd0 = open("/etc/group", 0); + if (fd0 < 0) { + perror("open /etc/group"); + exit(1); + } + + if (dup2(fd0, 11) < 0) { + perror("dup2->11"); + exit(1); + } + if (dup2(fd0, 19) < 0) { + perror("dup2->19"); + exit(1); + } + if (dup2(fd0, 99)< 0) { + perror("dup2->99 (ok)"); + } + if (dup2(fd0, 999) < 0) { + perror("dup3->999 (ok)"); + } + + libclf_closefrom(11); + for (i = 0; i < 10000; i++) { + if (fcntl(i, F_GETFL) != -1) { + fprintf(stderr, "Descriptor %d is still open", i); + if (i < 11) + fprintf(stderr, " (OK)\n"); + else + fprintf(stderr, " (BAD)\n"); + } + } + exit(0); +} + +#endif diff --git a/src/utils/closefrom.h b/src/utils/closefrom.h new file mode 100644 index 00000000..b0391f82 --- /dev/null +++ b/src/utils/closefrom.h @@ -0,0 +1,8 @@ +#ifndef _closefrom_h_included_ +#define _closefrom_h_included_ +/* @(#$Id: $ (C) 2004 J.F.Dockes */ + +/* Close all descriptors >= fd */ +extern int libclf_closefrom(int fd); + +#endif /* _closefrom_h_included_ */