Commit 78438ce1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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.
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −1
Original line number Diff line number Diff line
@@ -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)
+13 −11
Original line number Diff line number Diff line
@@ -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,13 +1917,11 @@ 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;
}
EXPORT_SYMBOL(d_make_root);
@@ -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;
+0 −2
Original line number Diff line number Diff line
@@ -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 *);

/*
Loading