]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Apply *_CLOEXEC flags to system calls which can be used with
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Tue, 2 Oct 2012 04:58:52 +0000 (13:58 +0900)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Tue, 2 Oct 2012 06:59:27 +0000 (15:59 +0900)
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.

CMakeLists.txt
build/cmake/config.h.in
configure.ac
libarchive/archive_private.h
libarchive/archive_read_disk_entry_from_file.c
libarchive/archive_read_disk_posix.c
libarchive/archive_read_open_filename.c
libarchive/archive_read_support_format_mtree.c
libarchive/archive_util.c
libarchive/archive_write_disk_posix.c
libarchive/archive_write_open_filename.c

index 9d6f56d6314dfc9d9ddd02573ce47d4847797043..662de0c4cc32c82a83a5fa7c5ed3c3f724f95f24 100644 (file)
@@ -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)
index b56d2535b6d7dc01c93e684605f9c771ad1659d0..d1e92a35bd6be51e444f5c80c1c50689897dad91 100644 (file)
@@ -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 <dlfcn.h> header file. */
 #cmakedefine HAVE_DLFCN_H 1
 
index d097b3b8172cede10f08510d1286842b4ebe5a92..0e2e0fd882512cbbb5afef320e251d9f0c0b1ab7 100644 (file)
@@ -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])
index 470f50051e7043ff6d86f7d953c25a92e0e7675d..30d472fcda0f09803f8b6e27557f9d8bcfa848d9 100644 (file)
@@ -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 *);
index 638c68b5aa7fde21c66a669d5cae51f129cec72c..e10172ef3afabbd66a65aaba0b39dba4ea5df171 100644 (file)
@@ -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, &copyfile_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;
        }
 
index 9b019b1546f855edd70641e57fffa99b616dd98c..8b50cc4262e9512ec01c5271ba80960e8d613c3b 100644 (file)
@@ -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;
index 802ecd70e483febf0a6223ceb6212d06ad5dec7c..00314c45ca0cb52c4622ccb579cd1e75bf98a035 100644 (file)
@@ -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);
index 370a2f46dfb97a4aa2df1783fdd6334c9f0e8d60..7fd9e466164b10dab69e1120fdc9a2691b7ecf6c 100644 (file)
@@ -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)) {
index 103c009d5bc8ca303a9d07761c08abaffaa48a11..ffeaac9c3a6727781f3e21380eb6f714c563e8cd 100644 (file)
@@ -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
+}
index 4663cec4f6c7788a3b57ba0c7955bf45aedc33b9..38997e46bccc0930649aec3ef93537757ee424ad 100644 (file)
@@ -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);
 
index bcde200d46b9adc08e853fe5fcfcac4f47b90a92..196b770e8e834951b1d3776306ca25707f26680d 100644 (file)
@@ -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)