Merge branch 'stable-fodder' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 May 2019 18:17:26 +0000 (11:17 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 May 2019 18:17:26 +0000 (11:17 -0700)
Pull vfs stable fodder fixes from Al Viro:

 - acct_on() fix for deadlock caught by overlayfs folks

 - autofs RCU use-after-free SNAFU (->d_manage() can be called
   locklessly, so we need to RCU-delay freeing the objects it looks at)

 - (hopefully) the end of "do we need freeing this dentry RCU-delayed"
   whack-a-mole.

* 'stable-fodder' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  autofs: fix use-after-free in lockless ->d_manage()
  dcache: sort the freeing-without-RCU-delay mess for good.
  acct_on(): don't mess with freeze protection

Documentation/filesystems/porting
fs/autofs/autofs_i.h
fs/autofs/inode.c
fs/dcache.c
fs/internal.h
fs/nsfs.c
include/linux/dcache.h
include/linux/mount.h
kernel/acct.c

index b8d3ddd..d392d4b 100644 (file)
@@ -663,3 +663,8 @@ in your dentry operations instead.
        there, but that's it.  Freeing memory in the callback is fine; doing
        more than that is possible, but requires a lot of care and is best
        avoided.
+--
+[mandatory]
+       DCACHE_RCUACCESS is gone; having an RCU delay on dentry freeing is the
+       default.  DCACHE_NORCU opts out, and only d_alloc_pseudo() has any
+       business doing so.
index 70c132a..e109131 100644 (file)
@@ -71,6 +71,7 @@ struct autofs_info {
 
        kuid_t uid;
        kgid_t gid;
+       struct rcu_head rcu;
 };
 
 #define AUTOFS_INF_EXPIRING    (1<<0) /* dentry in the process of expiring */
index 80597b8..fb0225f 100644 (file)
@@ -36,7 +36,7 @@ void autofs_clean_ino(struct autofs_info *ino)
 
 void autofs_free_ino(struct autofs_info *ino)
 {
-       kfree(ino);
+       kfree_rcu(ino, rcu);
 }
 
 void autofs_kill_sb(struct super_block *sb)
index aac41ad..c663c60 100644 (file)
@@ -344,7 +344,7 @@ static void dentry_free(struct dentry *dentry)
                }
        }
        /* if dentry was never visible to RCU, immediate free is OK */
-       if (!(dentry->d_flags & DCACHE_RCUACCESS))
+       if (dentry->d_flags & DCACHE_NORCU)
                __d_free(&dentry->d_u.d_rcu);
        else
                call_rcu(&dentry->d_u.d_rcu, __d_free);
@@ -1701,7 +1701,6 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
        struct dentry *dentry = __d_alloc(parent->d_sb, name);
        if (!dentry)
                return NULL;
-       dentry->d_flags |= DCACHE_RCUACCESS;
        spin_lock(&parent->d_lock);
        /*
         * don't need child lock because it is not subject
@@ -1726,7 +1725,7 @@ struct dentry *d_alloc_cursor(struct dentry * parent)
 {
        struct dentry *dentry = d_alloc_anon(parent->d_sb);
        if (dentry) {
-               dentry->d_flags |= DCACHE_RCUACCESS | DCACHE_DENTRY_CURSOR;
+               dentry->d_flags |= DCACHE_DENTRY_CURSOR;
                dentry->d_parent = dget(parent);
        }
        return dentry;
@@ -1739,10 +1738,17 @@ struct dentry *d_alloc_cursor(struct dentry * parent)
  *
  * For a filesystem that just pins its dentries in memory and never
  * performs lookups at all, return an unhashed IS_ROOT dentry.
+ * This is used for pipes, sockets et.al. - the stuff that should
+ * never be anyone's children or parents.  Unlike all other
+ * dentries, these will not have RCU delay between dropping the
+ * last reference and freeing them.
  */
 struct dentry *d_alloc_pseudo(struct super_block *sb, const struct qstr *name)
 {
-       return __d_alloc(sb, name);
+       struct dentry *dentry = __d_alloc(sb, name);
+       if (likely(dentry))
+               dentry->d_flags |= DCACHE_NORCU;
+       return dentry;
 }
 EXPORT_SYMBOL(d_alloc_pseudo);
 
@@ -1911,12 +1917,10 @@ struct dentry *d_make_root(struct inode *root_inode)
 
        if (root_inode) {
                res = d_alloc_anon(root_inode->i_sb);
-               if (res) {
-                       res->d_flags |= DCACHE_RCUACCESS;
+               if (res)
                        d_instantiate(res, root_inode);
-               } else {
+               else
                        iput(root_inode);
-               }
        }
        return res;
 }
@@ -2781,9 +2785,7 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
                copy_name(dentry, target);
                target->d_hash.pprev = NULL;
                dentry->d_parent->d_lockref.count++;
-               if (dentry == old_parent)
-                       dentry->d_flags |= DCACHE_RCUACCESS;
-               else
+               if (dentry != old_parent) /* wasn't IS_ROOT */
                        WARN_ON(!--old_parent->d_lockref.count);
        } else {
                target->d_parent = old_parent;
index 6a8b716..2e73628 100644 (file)
@@ -89,9 +89,7 @@ extern int sb_prepare_remount_readonly(struct super_block *);
 
 extern void __init mnt_init(void);
 
-extern int __mnt_want_write(struct vfsmount *);
 extern int __mnt_want_write_file(struct file *);
-extern void __mnt_drop_write(struct vfsmount *);
 extern void __mnt_drop_write_file(struct file *);
 
 /*
index 60702d6..30d150a 100644 (file)
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -85,13 +85,12 @@ slow:
        inode->i_fop = &ns_file_operations;
        inode->i_private = ns;
 
-       dentry = d_alloc_pseudo(mnt->mnt_sb, &empty_name);
+       dentry = d_alloc_anon(mnt->mnt_sb);
        if (!dentry) {
                iput(inode);
                return ERR_PTR(-ENOMEM);
        }
        d_instantiate(dentry, inode);
-       dentry->d_flags |= DCACHE_RCUACCESS;
        dentry->d_fsdata = (void *)ns->ops;
        d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry);
        if (d) {
index 60996e6..6e1e8e6 100644 (file)
@@ -176,7 +176,6 @@ struct dentry_operations {
       * typically using d_splice_alias. */
 
 #define DCACHE_REFERENCED              0x00000040 /* Recently used, don't discard. */
-#define DCACHE_RCUACCESS               0x00000080 /* Entry has ever been RCU-visible */
 
 #define DCACHE_CANT_MOUNT              0x00000100
 #define DCACHE_GENOCIDE                        0x00000200
@@ -217,6 +216,7 @@ struct dentry_operations {
 
 #define DCACHE_PAR_LOOKUP              0x10000000 /* being looked up (with parent locked shared) */
 #define DCACHE_DENTRY_CURSOR           0x20000000
+#define DCACHE_NORCU                   0x40000000 /* No RCU delay for freeing */
 
 extern seqlock_t rename_lock;
 
index 9197ddb..bf8cc41 100644 (file)
@@ -87,6 +87,8 @@ extern bool mnt_may_suid(struct vfsmount *mnt);
 
 struct path;
 extern struct vfsmount *clone_private_mount(const struct path *path);
+extern int __mnt_want_write(struct vfsmount *);
+extern void __mnt_drop_write(struct vfsmount *);
 
 struct file_system_type;
 extern struct vfsmount *fc_mount(struct fs_context *fc);
index addf773..81f9831 100644 (file)
@@ -227,7 +227,7 @@ static int acct_on(struct filename *pathname)
                filp_close(file, NULL);
                return PTR_ERR(internal);
        }
-       err = mnt_want_write(internal);
+       err = __mnt_want_write(internal);
        if (err) {
                mntput(internal);
                kfree(acct);
@@ -252,7 +252,7 @@ static int acct_on(struct filename *pathname)
        old = xchg(&ns->bacct, &acct->pin);
        mutex_unlock(&acct->lock);
        pin_kill(old);
-       mnt_drop_write(mnt);
+       __mnt_drop_write(mnt);
        mntput(mnt);
        return 0;
 }