Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4353660 authored by Al Viro's avatar Al Viro Committed by Greg Kroah-Hartman
Browse files

lock_parent() needs to recheck if dentry got __dentry_kill'ed under it



commit 3b821409632ab778d46e807516b457dfa72736ed upstream.

In case when dentry passed to lock_parent() is protected from freeing only
by the fact that it's on a shrink list and trylock of parent fails, we
could get hit by __dentry_kill() (and subsequent dentry_kill(parent))
between unlocking dentry and locking presumed parent.  We need to recheck
that dentry is alive once we lock both it and parent *and* postpone
rcu_read_unlock() until after that point.  Otherwise we could return
a pointer to struct dentry that already is rcu-scheduled for freeing, with
->d_lock held on it; caller's subsequent attempt to unlock it can end
up with memory corruption.

Cc: stable@vger.kernel.org # 3.12+, counting backports
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b62e31c1
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -581,11 +581,16 @@ again:
		spin_unlock(&parent->d_lock);
		goto again;
	}
	rcu_read_unlock();
	if (parent != dentry)
	if (parent != dentry) {
		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
	else
		if (unlikely(dentry->d_lockref.count < 0)) {
			spin_unlock(&parent->d_lock);
			parent = NULL;
		}
	} else {
		parent = NULL;
	}
	rcu_read_unlock();
	return parent;
}