From: Michihiro NAKAJIMA Date: Tue, 2 Oct 2012 04:58:52 +0000 (+0900) Subject: Apply *_CLOEXEC flags to system calls which can be used with X-Git-Tag: v3.1.0~40^2~121 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60141df1e89400928525f3c915be085f68c1ff1a;p=thirdparty%2Flibarchive.git Apply *_CLOEXEC flags to system calls which can be used with to avoid file descriptor leaks to the child process. - Pass O_CLOEXEC to open(2) system call if it is defined. - Use fcntl(2) and F_DUPFD_CLOEXEC instead of dup(2) if F_DUPFD_CLOEXEC is defined. - Set FD_CLOEXEC with fcntl(2) if a file descriptor does not have. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d6f56d63..662de0c4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -904,6 +904,7 @@ CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS) CHECK_FUNCTION_EXISTS_GLIBC(chown HAVE_CHOWN) CHECK_FUNCTION_EXISTS_GLIBC(chroot HAVE_CHROOT) CHECK_FUNCTION_EXISTS_GLIBC(ctime_r HAVE_CTIME_R) +CHECK_FUNCTION_EXISTS_GLIBC(dirfd HAVE_DIRFD) CHECK_FUNCTION_EXISTS_GLIBC(fchdir HAVE_FCHDIR) CHECK_FUNCTION_EXISTS_GLIBC(fchflags HAVE_FCHFLAGS) CHECK_FUNCTION_EXISTS_GLIBC(fchmod HAVE_FCHMOD) diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index b56d2535b..d1e92a35b 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -390,6 +390,9 @@ typedef uint64_t uintmax_t; */ #cmakedefine HAVE_DIRENT_H 1 +/* Define to 1 if you have the `dirfd' function. */ +#cmakedefine HAVE_DIRFD 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DLFCN_H 1 diff --git a/configure.ac b/configure.ac index d097b3b81..0e2e0fd88 100644 --- a/configure.ac +++ b/configure.ac @@ -433,7 +433,7 @@ AC_FUNC_VPRINTF # To avoid necessity for including windows.h or special forward declaration # workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *' AC_CHECK_STDCALL_FUNC([CreateHardLinkA],[const char *, const char *, void *]) -AC_CHECK_FUNCS([chflags chown chroot ctime_r]) +AC_CHECK_FUNCS([chflags chown chroot ctime_r dirfd]) AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fork]) AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate]) AC_CHECK_FUNCS([futimens futimes futimesat]) diff --git a/libarchive/archive_private.h b/libarchive/archive_private.h index 470f50051..30d472fcd 100644 --- a/libarchive/archive_private.h +++ b/libarchive/archive_private.h @@ -134,6 +134,7 @@ int __archive_check_magic(struct archive *, unsigned int magic, void __archive_errx(int retvalue, const char *msg) __LA_DEAD; +void __archive_ensure_cloexec_flag(int fd); int __archive_mktemp(const char *tmpdir); int __archive_clean(struct archive *); diff --git a/libarchive/archive_read_disk_entry_from_file.c b/libarchive/archive_read_disk_entry_from_file.c index 638c68b5a..e10172ef3 100644 --- a/libarchive/archive_read_disk_entry_from_file.c +++ b/libarchive/archive_read_disk_entry_from_file.c @@ -104,6 +104,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_disk_entry_from_file.c 2010 #include "archive_private.h" #include "archive_read_disk_private.h" +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + /* * Linux and FreeBSD plug this obvious hole in POSIX.1e in * different ways. @@ -193,9 +197,11 @@ archive_read_disk_entry_from_file(struct archive *_a, if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, - O_RDONLY | O_NONBLOCK); + O_RDONLY | O_NONBLOCK | O_CLOEXEC); else - fd = open(path, O_RDONLY | O_NONBLOCK); + fd = open(path, O_RDONLY | O_NONBLOCK | + O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; @@ -335,13 +341,14 @@ setup_mac_metadata(struct archive_read_disk *a, ret = ARCHIVE_WARN; goto cleanup; } - tempfd = open(tempfile, O_RDONLY); + tempfd = open(tempfile, O_RDONLY | O_CLOEXEC); if (tempfd < 0) { archive_set_error(&a->archive, errno, "Could not open extended attribute file"); ret = ARCHIVE_WARN; goto cleanup; } + __archive_ensure_cloexec_flag(tempfd); if (fstat(tempfd, ©file_stat)) { archive_set_error(&a->archive, errno, "Could not check size of extended attributes"); @@ -1035,14 +1042,15 @@ setup_sparse(struct archive_read_disk *a, path = archive_entry_pathname(entry); if (a->tree != NULL) *fd = a->open_on_current_dir(a->tree, path, - O_RDONLY | O_NONBLOCK); + O_RDONLY | O_NONBLOCK | O_CLOEXEC); else - *fd = open(path, O_RDONLY | O_NONBLOCK); + *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } + __archive_ensure_cloexec_flag(*fd); } /* Initialize buffer to avoid the error valgrind complains about. */ @@ -1148,12 +1156,13 @@ setup_sparse(struct archive_read_disk *a, if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0) return (ARCHIVE_OK); - *fd = open(path, O_RDONLY | O_NONBLOCK); + *fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (*fd < 0) { archive_set_error(&a->archive, errno, "Can't open `%s'", path); return (ARCHIVE_FAILED); } + __archive_ensure_cloexec_flag(*fd); initial_off = 0; } diff --git a/libarchive/archive_read_disk_posix.c b/libarchive/archive_read_disk_posix.c index 9b019b154..8b50cc426 100644 --- a/libarchive/archive_read_disk_posix.c +++ b/libarchive/archive_read_disk_posix.c @@ -105,6 +105,9 @@ __FBSDID("$FreeBSD$"); #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif /*- * This is a new directory-walking system that addresses a number @@ -361,6 +364,7 @@ static int setup_sparse(struct archive_read_disk *, struct archive_entry *); static int close_and_restore_time(int fd, struct tree *, struct restore_time *); static int open_on_current_dir(struct tree *, const char *, int); +static int tree_dup(int); static struct archive_vtable * @@ -717,7 +721,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, * Open the current file. */ if (t->entry_fd < 0) { - int flags = O_RDONLY | O_BINARY; + int flags = O_RDONLY | O_BINARY | O_CLOEXEC; /* * Eliminate or reduce cache effects if we can. @@ -740,6 +744,7 @@ _archive_read_data_block(struct archive *_a, const void **buff, #endif t->entry_fd = open_on_current_dir(t, tree_current_access_path(t), flags); + __archive_ensure_cloexec_flag(t->entry_fd); #if defined(O_NOATIME) /* * When we did open the file with O_NOATIME flag, @@ -987,7 +992,9 @@ next_entry(struct archive_read_disk *a, struct tree *t, int stflags; t->entry_fd = open_on_current_dir(t, - tree_current_access_path(t), O_RDONLY | O_NONBLOCK); + tree_current_access_path(t), + O_RDONLY | O_NONBLOCK | O_CLOEXEC); + __archive_ensure_cloexec_flag(t->entry_fd); if (t->entry_fd >= 0) { r = ioctl(t->entry_fd, EXT2_IOC_GETFLAGS, &stflags); @@ -1484,7 +1491,8 @@ setup_current_filesystem(struct archive_read_disk *a) * where current is. */ int fd = openat(tree_current_dir_fd(t), - tree_current_access_path(t), O_RDONLY); + tree_current_access_path(t), O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); @@ -1662,7 +1670,8 @@ setup_current_filesystem(struct archive_read_disk *a) * where current is. */ int fd = openat(tree_current_dir_fd(t), - tree_current_access_path(t), O_RDONLY); + tree_current_access_path(t), O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); @@ -1770,7 +1779,8 @@ setup_current_filesystem(struct archive_read_disk *a) * where current is. */ int fd = openat(tree_current_dir_fd(t), - tree_current_access_path(t), O_RDONLY); + tree_current_access_path(t), O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(&a->archive, errno, "openat failed"); @@ -1954,6 +1964,28 @@ open_on_current_dir(struct tree *t, const char *path, int flags) #endif } +static int +tree_dup(int fd) +{ + int new_fd; +#ifdef F_DUPFD_CLOEXEC + static volatile int can_dupfd_cloexec = 1; + + if (can_dupfd_cloexec) { + new_fd = fcntl(fd, F_DUPFD_CLOEXEC); + if (new_fd != -1) + return (new_fd); + /* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC, + * but it cannot be used. So we have to try dup(). */ + /* We won't try F_DUPFD_CLOEXEC. */ + can_dupfd_cloexec = 0; + } +#endif /* F_DUPFD_CLOEXEC */ + new_fd = dup(fd); + __archive_ensure_cloexec_flag(new_fd); + return (new_fd); +} + /* * Add a directory path to the current stack. */ @@ -2054,8 +2086,9 @@ tree_reopen(struct tree *t, const char *path, int restore_time) tree_push(t, path, 0, 0, 0, NULL); t->stack->flags = needsFirstVisit; t->maxOpenCount = t->openCount = 1; - t->initial_dir_fd = open(".", O_RDONLY); - t->working_dir_fd = dup(t->initial_dir_fd); + t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(t->initial_dir_fd); + t->working_dir_fd = tree_dup(t->initial_dir_fd); return (t); } @@ -2065,11 +2098,12 @@ tree_descent(struct tree *t) int flag, new_fd, r = 0; t->dirname_length = archive_strlen(&t->path); - flag = O_RDONLY; + flag = O_RDONLY | O_CLOEXEC; #if defined(O_DIRECTORY) flag |= O_DIRECTORY; #endif new_fd = open_on_current_dir(t, t->stack->name.s, flag); + __archive_ensure_cloexec_flag(new_fd); if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_DIR; @@ -2103,8 +2137,10 @@ tree_ascend(struct tree *t) prev_dir_fd = t->working_dir_fd; if (te->flags & isDirLink) new_fd = te->symlink_parent_fd; - else - new_fd = open_on_current_dir(t, "..", O_RDONLY); + else { + new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(new_fd); + } if (new_fd < 0) { t->tree_errno = errno; r = TREE_ERROR_FATAL; @@ -2267,11 +2303,16 @@ tree_dir_next_posix(struct tree *t) #endif #if defined(HAVE_FDOPENDIR) - if ((t->d = fdopendir(dup(t->working_dir_fd))) == NULL) { -#else - if (tree_enter_working_dir(t) != 0 || - (t->d = opendir(".")) == NULL) { + t->d = fdopendir(tree_dup(t->working_dir_fd)); +#else /* HAVE_FDOPENDIR */ + if (tree_enter_working_dir(t) == 0) { + t->d = opendir("."); +#if HAVE_DIRFD || defined(dirfd) + __archive_ensure_cloexec_flag(dirfd(t->d)); #endif + } +#endif /* HAVE_FDOPENDIR */ + if (t->d == NULL) { r = tree_ascend(t); /* Undo "chdir" */ tree_pop(t); t->tree_errno = errno; diff --git a/libarchive/archive_read_open_filename.c b/libarchive/archive_read_open_filename.c index 802ecd70e..00314c45c 100644 --- a/libarchive/archive_read_open_filename.c +++ b/libarchive/archive_read_open_filename.c @@ -60,11 +60,15 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_open_filename.c 201093 2009 #endif #include "archive.h" +#include "archive_private.h" #include "archive_string.h" #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif struct read_file_data { int fd; @@ -244,7 +248,8 @@ file_open(struct archive *a, void *client_data) filename = ""; } else if (mine->filename_type == FNT_MBS) { filename = mine->filename.m; - fd = open(filename, O_RDONLY | O_BINARY); + fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) { archive_set_error(a, errno, "Failed to open '%s'", filename); diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index 370a2f46d..7fd9e4661 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -55,6 +55,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_mtree.c 2011 #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif #define MTREE_HAS_DEVICE 0x0001 #define MTREE_HAS_FFLAGS 0x0002 @@ -1119,7 +1122,8 @@ parse_file(struct archive_read *a, struct archive_entry *entry, if (archive_entry_filetype(entry) == AE_IFREG || archive_entry_filetype(entry) == AE_IFDIR) { - mtree->fd = open(path, O_RDONLY | O_BINARY); + mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(mtree->fd); if (mtree->fd == -1 && (errno != ENOENT || archive_strlen(&mtree->contents_name) > 0)) { diff --git a/libarchive/archive_util.c b/libarchive/archive_util.c index 103c009d5..ffeaac9c3 100644 --- a/libarchive/archive_util.c +++ b/libarchive/archive_util.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009,2010 Michihiro NAKAJIMA + * Copyright (c) 2009-2012 Michihiro NAKAJIMA * Copyright (c) 2003-2007 Tim Kientzle * All rights reserved. * @@ -50,6 +50,10 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:1 #include "archive_private.h" #include "archive_string.h" +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + /* Generic initialization of 'struct archive' objects. */ int __archive_clean(struct archive *a) @@ -385,6 +389,7 @@ __archive_mktemp(const char *tmpdir) fd = mkstemp(temp_name.s); if (fd < 0) goto exit_tmpfile; + __archive_ensure_cloexec_flag(fd); unlink(temp_name.s); exit_tmpfile: archive_string_free(&temp_name); @@ -438,7 +443,8 @@ __archive_mktemp(const char *tmpdir) archive_strcat(&temp_name, "XXXXXXXXXX"); ep = temp_name.s + archive_strlen(&temp_name); - fd = open("/dev/random", O_RDONLY); + fd = open("/dev/random", O_RDONLY | O_CLOEXEC); + __archive_ensure_cloexec_flag(fd); if (fd < 0) seed = time(NULL); else { @@ -452,10 +458,12 @@ __archive_mktemp(const char *tmpdir) p = tp; while (p < ep) *p++ = num[((unsigned)rand_r(&seed)) % sizeof(num)]; - fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR, 0600); + fd = open(temp_name.s, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, + 0600); } while (fd < 0 && errno == EEXIST); if (fd < 0) goto exit_tmpfile; + __archive_ensure_cloexec_flag(fd); unlink(temp_name.s); exit_tmpfile: archive_string_free(&temp_name); @@ -464,3 +472,29 @@ exit_tmpfile: #endif /* HAVE_MKSTEMP */ #endif /* !_WIN32 || __CYGWIN__ */ + +/* + * Set FD_CLOEXEC flag to a file descriptor if it is not set. + * We have to set the flag if the platform does not provide O_CLOEXEC + * or F_DUPFD_CLOEXEC flags. + * + * Note: This function is absolutely called after creating a new file + * descriptor even if the platform seemingly provides O_CLOEXEC or + * F_DUPFD_CLOEXEC macros because it is possible that the platform + * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it. + */ +void +__archive_ensure_cloexec_flag(int fd) +{ +#if defined(_WIN32) && !defined(__CYGWIN__) + (void)fd; /* UNSED */ +#else + int flags; + + if (fd >= 0) { + flags = fcntl(fd, F_GETFD); + if (flags != -1 && (flags & FD_CLOEXEC) == 0) + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } +#endif +} diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index 4663cec4f..38997e46b 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -131,6 +131,9 @@ __FBSDID("$FreeBSD$"); #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif struct fixup_entry { struct fixup_entry *next; @@ -1012,7 +1015,8 @@ edit_deep_directories(struct archive_write_disk *a) return; /* Try to record our starting dir. */ - a->restore_pwd = open(".", O_RDONLY | O_BINARY); + a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(a->restore_pwd); if (a->restore_pwd < 0) return; @@ -1237,7 +1241,9 @@ create_filesystem_object(struct archive_write_disk *a) a->todo = 0; a->deferred = 0; } else if (r == 0 && a->filesize > 0) { - a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY); + a->fd = open(a->name, + O_WRONLY | O_TRUNC | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(a->fd); if (a->fd < 0) r = errno; } @@ -1274,7 +1280,8 @@ create_filesystem_object(struct archive_write_disk *a) /* FALLTHROUGH */ case AE_IFREG: a->fd = open(a->name, - O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode); + O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode); + __archive_ensure_cloexec_flag(a->fd); r = (a->fd < 0); break; case AE_IFCHR: @@ -2425,8 +2432,10 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, return (ARCHIVE_OK); /* If we weren't given an fd, open it ourselves. */ - if (myfd < 0) - myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY); + if (myfd < 0) { + myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); + __archive_ensure_cloexec_flag(myfd); + } if (myfd < 0) return (ARCHIVE_OK); diff --git a/libarchive/archive_write_open_filename.c b/libarchive/archive_write_open_filename.c index bcde200d4..196b770e8 100644 --- a/libarchive/archive_write_open_filename.c +++ b/libarchive/archive_write_open_filename.c @@ -46,11 +46,15 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_open_filename.c 191165 200 #endif #include "archive.h" +#include "archive_private.h" #include "archive_string.h" #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif struct write_file_data { int fd; @@ -136,7 +140,7 @@ file_open(struct archive *a, void *client_data) const char *mbs; mine = (struct write_file_data *)client_data; - flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY; + flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC; /* * Open the file. @@ -171,6 +175,7 @@ file_open(struct archive *a, void *client_data) return (ARCHIVE_FATAL); } mine->fd = open(mbs, flags, 0666); + __archive_ensure_cloexec_flag(mine->fd); #endif if (mine->fd < 0) { if (mbs != NULL)