diff options
author | Tobias Brunner <tobias@strongswan.org> | 2015-06-11 12:19:56 +0200 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2015-08-17 11:19:44 +0200 |
commit | f25f4192c7ea7f3ccb6759dc000a3272e9c4e815 (patch) | |
tree | dc04a23bec7a325e79585fda1235ceae2cd341fd /src/libstrongswan/utils/utils.c | |
parent | b410d7f8ff39001a3fefa5bebd08de3c295f5c9c (diff) | |
download | strongswan-f25f4192c7ea7f3ccb6759dc000a3272e9c4e815.tar.bz2 strongswan-f25f4192c7ea7f3ccb6759dc000a3272e9c4e815.tar.xz |
utils: Directly use syscall() to close open FDs in closefrom()
This avoids any allocations, since calling malloc() after fork() is
potentially unsafe.
Fixes #990.
Diffstat (limited to 'src/libstrongswan/utils/utils.c')
-rw-r--r-- | src/libstrongswan/utils/utils.c | 58 |
1 files changed, 53 insertions, 5 deletions
diff --git a/src/libstrongswan/utils/utils.c b/src/libstrongswan/utils/utils.c index 55eab8e14..c396540ec 100644 --- a/src/libstrongswan/utils/utils.c +++ b/src/libstrongswan/utils/utils.c @@ -25,7 +25,22 @@ #endif #ifndef HAVE_CLOSEFROM +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) +# include <sys/stat.h> +# include <fcntl.h> +# include <sys/syscall.h> +/* This is from the kernel sources. We limit the length of directory names to + * 256 as we only use it to enumerate FDs. */ +struct linux_dirent64 { + u_int64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; +#else /* !defined(__linux__) || !defined(HAVE_SYS_SYSCALL_H) */ # include <dirent.h> +#endif /* defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) */ #endif #include <library.h> @@ -120,14 +135,46 @@ void wait_sigint() */ void closefrom(int low_fd) { + int max_fd, dir_fd, fd; + + /* try to close only open file descriptors on Linux... */ +#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) + /* By directly using a syscall we avoid any calls that might be unsafe after + * fork() (e.g. malloc()). */ + char buffer[sizeof(struct linux_dirent64)]; + struct linux_dirent64 *entry; + int offset, len; + + dir_fd = open("/proc/self/fd", O_RDONLY); + if (dir_fd != -1) + { + while ((len = syscall(SYS_getdents64, dir_fd, buffer, + sizeof(buffer))) > 0) + { + for (offset = 0; offset < len; offset += entry->d_reclen) + { + entry = (struct linux_dirent64*)(buffer + offset); + if (!isdigit(entry->d_name[0])) + { + continue; + } + fd = atoi(entry->d_name); + if (fd != dir_fd && fd >= low_fd) + { + close(fd); + } + } + } + close(dir_fd); + return; + } +#else /* !defined(__linux__) || !defined(HAVE_SYS_SYSCALL_H) */ + /* This is potentially unsafe when called after fork() in multi-threaded + * applications. In particular opendir() will require an allocation. + * Depends on how the malloc() implementation handles such situations. */ DIR *dir; struct dirent *entry; - int max_fd, dir_fd, fd; - /* try to close only open file descriptors on Linux... This is potentially - * unsafe when called after fork() in multi-threaded applications. In - * particular opendir() will require an allocation. So it depends on how - * the malloc() implementation handles such situations */ dir = opendir(FD_DIR); if (dir) { @@ -147,6 +194,7 @@ void closefrom(int low_fd) closedir(dir); return; } +#endif /* defined(__linux__) && defined(HAVE_SYS_SYSCALL_H) */ /* ...fall back to closing all fds otherwise */ #ifdef WIN32 |