Commit 78438ce1 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 168e153d ce285c26
...@@ -663,3 +663,8 @@ in your dentry operations instead. ...@@ -663,3 +663,8 @@ in your dentry operations instead.
there, but that's it. Freeing memory in the callback is fine; doing 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 more than that is possible, but requires a lot of care and is best
avoided. 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.
...@@ -71,6 +71,7 @@ struct autofs_info { ...@@ -71,6 +71,7 @@ struct autofs_info {
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
struct rcu_head rcu;
}; };
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry in the process of expiring */ #define AUTOFS_INF_EXPIRING (1<<0) /* dentry in the process of expiring */
......
...@@ -36,7 +36,7 @@ void autofs_clean_ino(struct autofs_info *ino) ...@@ -36,7 +36,7 @@ void autofs_clean_ino(struct autofs_info *ino)
void autofs_free_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) void autofs_kill_sb(struct super_block *sb)
......
...@@ -344,7 +344,7 @@ static void dentry_free(struct dentry *dentry) ...@@ -344,7 +344,7 @@ static void dentry_free(struct dentry *dentry)
} }
} }
/* if dentry was never visible to RCU, immediate free is OK */ /* 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); __d_free(&dentry->d_u.d_rcu);
else else
call_rcu(&dentry->d_u.d_rcu, __d_free); call_rcu(&dentry->d_u.d_rcu, __d_free);
...@@ -1701,7 +1701,6 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name) ...@@ -1701,7 +1701,6 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
struct dentry *dentry = __d_alloc(parent->d_sb, name); struct dentry *dentry = __d_alloc(parent->d_sb, name);
if (!dentry) if (!dentry)
return NULL; return NULL;
dentry->d_flags |= DCACHE_RCUACCESS;
spin_lock(&parent->d_lock); spin_lock(&parent->d_lock);
/* /*
* don't need child lock because it is not subject * don't need child lock because it is not subject
...@@ -1726,7 +1725,7 @@ struct dentry *d_alloc_cursor(struct dentry * parent) ...@@ -1726,7 +1725,7 @@ struct dentry *d_alloc_cursor(struct dentry * parent)
{ {
struct dentry *dentry = d_alloc_anon(parent->d_sb); struct dentry *dentry = d_alloc_anon(parent->d_sb);
if (dentry) { if (dentry) {
dentry->d_flags |= DCACHE_RCUACCESS | DCACHE_DENTRY_CURSOR; dentry->d_flags |= DCACHE_DENTRY_CURSOR;
dentry->d_parent = dget(parent); dentry->d_parent = dget(parent);
} }
return dentry; return dentry;
...@@ -1739,10 +1738,17 @@ struct dentry *d_alloc_cursor(struct dentry * parent) ...@@ -1739,10 +1738,17 @@ struct dentry *d_alloc_cursor(struct dentry * parent)
* *
* For a filesystem that just pins its dentries in memory and never * For a filesystem that just pins its dentries in memory and never
* performs lookups at all, return an unhashed IS_ROOT dentry. * 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) 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); EXPORT_SYMBOL(d_alloc_pseudo);
...@@ -1911,12 +1917,10 @@ struct dentry *d_make_root(struct inode *root_inode) ...@@ -1911,12 +1917,10 @@ struct dentry *d_make_root(struct inode *root_inode)
if (root_inode) { if (root_inode) {
res = d_alloc_anon(root_inode->i_sb); res = d_alloc_anon(root_inode->i_sb);
if (res) { if (res)
res->d_flags |= DCACHE_RCUACCESS;
d_instantiate(res, root_inode); d_instantiate(res, root_inode);
} else { else
iput(root_inode); iput(root_inode);
}
} }
return res; return res;
} }
...@@ -2781,9 +2785,7 @@ static void __d_move(struct dentry *dentry, struct dentry *target, ...@@ -2781,9 +2785,7 @@ static void __d_move(struct dentry *dentry, struct dentry *target,
copy_name(dentry, target); copy_name(dentry, target);
target->d_hash.pprev = NULL; target->d_hash.pprev = NULL;
dentry->d_parent->d_lockref.count++; dentry->d_parent->d_lockref.count++;
if (dentry == old_parent) if (dentry != old_parent) /* wasn't IS_ROOT */
dentry->d_flags |= DCACHE_RCUACCESS;
else
WARN_ON(!--old_parent->d_lockref.count); WARN_ON(!--old_parent->d_lockref.count);
} else { } else {
target->d_parent = old_parent; target->d_parent = old_parent;
......
...@@ -89,9 +89,7 @@ extern int sb_prepare_remount_readonly(struct super_block *); ...@@ -89,9 +89,7 @@ extern int sb_prepare_remount_readonly(struct super_block *);
extern void __init mnt_init(void); extern void __init mnt_init(void);
extern int __mnt_want_write(struct vfsmount *);
extern int __mnt_want_write_file(struct file *); extern int __mnt_want_write_file(struct file *);
extern void __mnt_drop_write(struct vfsmount *);
extern void __mnt_drop_write_file(struct file *); extern void __mnt_drop_write_file(struct file *);
/* /*
......
...@@ -85,13 +85,12 @@ static void *__ns_get_path(struct path *path, struct ns_common *ns) ...@@ -85,13 +85,12 @@ static void *__ns_get_path(struct path *path, struct ns_common *ns)
inode->i_fop = &ns_file_operations; inode->i_fop = &ns_file_operations;
inode->i_private = ns; inode->i_private = ns;
dentry = d_alloc_pseudo(mnt->mnt_sb, &empty_name); dentry = d_alloc_anon(mnt->mnt_sb);
if (!dentry) { if (!dentry) {
iput(inode); iput(inode);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
d_instantiate(dentry, inode); d_instantiate(dentry, inode);
dentry->d_flags |= DCACHE_RCUACCESS;
dentry->d_fsdata = (void *)ns->ops; dentry->d_fsdata = (void *)ns->ops;
d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry); d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry);
if (d) { if (d) {
......
...@@ -176,7 +176,6 @@ struct dentry_operations { ...@@ -176,7 +176,6 @@ struct dentry_operations {
* typically using d_splice_alias. */ * typically using d_splice_alias. */
#define DCACHE_REFERENCED 0x00000040 /* Recently used, don't discard. */ #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_CANT_MOUNT 0x00000100
#define DCACHE_GENOCIDE 0x00000200 #define DCACHE_GENOCIDE 0x00000200
...@@ -217,6 +216,7 @@ struct dentry_operations { ...@@ -217,6 +216,7 @@ struct dentry_operations {
#define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */ #define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */
#define DCACHE_DENTRY_CURSOR 0x20000000 #define DCACHE_DENTRY_CURSOR 0x20000000
#define DCACHE_NORCU 0x40000000 /* No RCU delay for freeing */
extern seqlock_t rename_lock; extern seqlock_t rename_lock;
......
...@@ -87,6 +87,8 @@ extern bool mnt_may_suid(struct vfsmount *mnt); ...@@ -87,6 +87,8 @@ extern bool mnt_may_suid(struct vfsmount *mnt);
struct path; struct path;
extern struct vfsmount *clone_private_mount(const struct path *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; struct file_system_type;
extern struct vfsmount *fc_mount(struct fs_context *fc); extern struct vfsmount *fc_mount(struct fs_context *fc);
......
...@@ -227,7 +227,7 @@ static int acct_on(struct filename *pathname) ...@@ -227,7 +227,7 @@ static int acct_on(struct filename *pathname)
filp_close(file, NULL); filp_close(file, NULL);
return PTR_ERR(internal); return PTR_ERR(internal);
} }
err = mnt_want_write(internal); err = __mnt_want_write(internal);
if (err) { if (err) {
mntput(internal); mntput(internal);
kfree(acct); kfree(acct);
...@@ -252,7 +252,7 @@ static int acct_on(struct filename *pathname) ...@@ -252,7 +252,7 @@ static int acct_on(struct filename *pathname)
old = xchg(&ns->bacct, &acct->pin); old = xchg(&ns->bacct, &acct->pin);
mutex_unlock(&acct->lock); mutex_unlock(&acct->lock);
pin_kill(old); pin_kill(old);
mnt_drop_write(mnt); __mnt_drop_write(mnt);
mntput(mnt); mntput(mnt);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment