libfuse: fix multiple close of device fd
authorMiklos Szeredi <mszeredi@suse.cz>
Thu, 20 Jun 2013 09:43:02 +0000 (11:43 +0200)
committerMiklos Szeredi <mszeredi@suse.cz>
Mon, 1 Jul 2013 08:06:37 +0000 (10:06 +0200)
- fuse_kern_unmount closes handle (e.g. 19)
- a thread in my process opens a file - the OS assigns newly freed
handle (i.e. 19)
- fuse_kern_chan_destroy closes the same handle (i.e. 19)
- a thread in my process opens another file - the OS assigns newly
freed handle (i.e. 19)
- * MAYHEM *

Reported by Dan Greenfield

ChangeLog
lib/fuse_i.h
lib/fuse_session.c
lib/helper.c
lib/mount.c
lib/mount_bsd.c

index 3afa404a7cb5624baf2804000e74e49bac4be1de..2e902f591c1c4a0f1ae6e0264da144902444730e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2013-06-20  Miklos Szeredi <miklos@szeredi.hu>
+
+       * libfuse: fix multiple close of device fd.  Reported by Dan
+       Greenfield
+
 2013-03-19  Miklos Szeredi <miklos@szeredi.hu>
 
        * libfuse: fix thread cancel race.  Exiting a worker my race with
index 78f14677aa2f2934e74e21a134e9a6cc9c4d9a29..fa3715606d809760224888d1b82a1f9aec3112ae 100644 (file)
@@ -106,6 +106,8 @@ struct fuse_session *fuse_lowlevel_new_common(struct fuse_args *args,
                                        size_t op_size, void *userdata);
 
 void fuse_kern_unmount_compat22(const char *mountpoint);
+int fuse_chan_clearfd(struct fuse_chan *ch);
+
 void fuse_kern_unmount(const char *mountpoint, int fd);
 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args);
 
index c55f250747172582028a834a78af8eaf452c6b83..6e110683380ca8dc5abe2d3f7d689a134ed38d63 100644 (file)
@@ -182,6 +182,13 @@ int fuse_chan_fd(struct fuse_chan *ch)
        return ch->fd;
 }
 
+int fuse_chan_clearfd(struct fuse_chan *ch)
+{
+       int fd = ch->fd;
+       ch->fd = -1;
+       return fd;
+}
+
 size_t fuse_chan_bufsize(struct fuse_chan *ch)
 {
        return ch->bufsize;
index ace19dd7098bf50027c22c7a6aae5b3c455b58bf..b64401296503b847d8b1cfbbb39f878d5a57631b 100644 (file)
@@ -249,10 +249,12 @@ struct fuse_chan *fuse_mount(const char *mountpoint, struct fuse_args *args)
 
 static void fuse_unmount_common(const char *mountpoint, struct fuse_chan *ch)
 {
-       int fd = ch ? fuse_chan_fd(ch) : -1;
-       fuse_kern_unmount(mountpoint, fd);
-       if (ch)
-               fuse_chan_destroy(ch);
+       if (mountpoint) {
+               int fd = ch ? fuse_chan_clearfd(ch) : -1;
+               fuse_kern_unmount(mountpoint, fd);
+               if (ch)
+                       fuse_chan_destroy(ch);
+       }
 }
 
 void fuse_unmount(const char *mountpoint, struct fuse_chan *ch)
index 6a9da9eefd5641fc2b790738a1e739525016e5b5..0f767c8ff2237c59e8c93e9f87eb25ce5b4a310a 100644 (file)
@@ -300,14 +300,18 @@ void fuse_kern_unmount(const char *mountpoint, int fd)
                pfd.fd = fd;
                pfd.events = 0;
                res = poll(&pfd, 1, 0);
+
+               /* Need to close file descriptor, otherwise synchronous umount
+                  would recurse into filesystem, and deadlock.
+
+                  Caller expects fuse_kern_unmount to close the fd, so close it
+                  anyway. */
+               close(fd);
+
                /* If file poll returns POLLERR on the device file descriptor,
                   then the filesystem is already unmounted */
                if (res == 1 && (pfd.revents & POLLERR))
                        return;
-
-               /* Need to close file descriptor, otherwise synchronous umount
-                  would recurse into filesystem, and deadlock */
-               close(fd);
        }
 
        if (geteuid() == 0) {
index 62443ac25f99d4a83d7c1748a8049f12faee76c0..3aec3e3eb2b1a59e40b3eb246c07854848611155 100644 (file)
@@ -228,18 +228,21 @@ void fuse_kern_unmount(const char *mountpoint, int fd)
        (void)mountpoint;
 
        if (fstat(fd, &sbuf) == -1)
-               return;
+               goto out;
 
        devname_r(sbuf.st_rdev, S_IFCHR, dev, 128);
 
        if (strncmp(dev, "fuse", 4))
-               return;
+               goto out;
 
        strtol(dev + 4, &ep, 10);
        if (*ep != '\0')
-               return;
+               goto out;
 
        do_unmount(dev, fd);
+
+out:
+       close(fd);
 }
 
 /* Check if kernel is doing init in background */