aboutsummaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-05-07 11:17:26 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-05-07 11:17:26 -0700
commit78438ce18f26dbcaa8993bb45d20ffb0cec3bc3e (patch)
tree2f6fe2eb05296a410a44ee7d602cafe2d202467b /fs
parent168e153d5ebbdd6a3fa85db1cc4879ed4b7030e0 (diff)
parentce285c267a003acbf607f3540ff71287f82e5282 (diff)
Merge branch 'stable-fodder' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
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
Diffstat (limited to 'fs')
-rw-r--r--fs/autofs/autofs_i.h1
-rw-r--r--fs/autofs/inode.c2
-rw-r--r--fs/dcache.c24
-rw-r--r--fs/internal.h2
-rw-r--r--fs/nsfs.c3
5 files changed, 16 insertions, 16 deletions
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
index 70c132acdab1..e1091312abe1 100644
--- a/fs/autofs/autofs_i.h
+++ b/fs/autofs/autofs_i.h
@@ -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 */
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
index 80597b88718b..fb0225f21c12 100644
--- a/fs/autofs/inode.c
+++ b/fs/autofs/inode.c
@@ -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)
diff --git a/fs/dcache.c b/fs/dcache.c
index aac41adf4743..c663c602f9ef 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -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;
diff --git a/fs/internal.h b/fs/internal.h
index 6a8b71643af4..2e7362837a6e 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -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 *);
/*
diff --git a/fs/nsfs.c b/fs/nsfs.c
index 60702d677bd4..30d150a4f0c6 100644
--- 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) {