From: Al Viro Date: Sat, 24 Feb 2018 01:47:17 +0000 (-0500) Subject: lock_parent() needs to recheck if dentry got __dentry_kill'ed under it X-Git-Tag: v4.4.123~13 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8dc6893e8fadbd63c1549f411c20c96c53e3381f;p=profile%2Fcommon%2Fplatform%2Fkernel%2Flinux-artik7.git 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: Al Viro Signed-off-by: Greg Kroah-Hartman --- diff --git a/fs/dcache.c b/fs/dcache.c index 3ed642e..751a0d8 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -634,11 +634,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; }