[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <lsq.1528380321.767618631@decadent.org.uk>
Date: Thu, 07 Jun 2018 15:05:21 +0100
From: Ben Hutchings <ben@...adent.org.uk>
To: linux-kernel@...r.kernel.org, stable@...r.kernel.org
CC: akpm@...ux-foundation.org, "Al Viro" <viro@...iv.linux.org.uk>
Subject: [PATCH 3.16 280/410] lock_parent() needs to recheck if dentry got
__dentry_kill'ed under it
3.16.57-rc1 review patch. If anyone has any objections, please let me know.
------------------
From: Al Viro <viro@...iv.linux.org.uk>
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.
Signed-off-by: Al Viro <viro@...iv.linux.org.uk>
Signed-off-by: Ben Hutchings <ben@...adent.org.uk>
---
fs/dcache.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -590,11 +590,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;
}
Powered by blists - more mailing lists