From: NeilBrown <neilb@suse.de>
To: Andrew Morton <akpm@osdl.org>
Cc: nfs@lists.sourceforge.net, linux-kernel@vger.kernel.org
Subject: [PATCH 004 of 8] knfsd: Close a race-opportunity in d_splice_alias
Date: Fri, 29 Sep 2006 13:08:58 +1000 [thread overview]
Message-ID: <1060929030858.24062@suse.de> (raw)
In-Reply-To: 20060929130518.23919.patches@notabene
There is a possible race in d_splice_alias.
Though __d_find_alias(inode, 1) will only return a dentry
with DCACHE_DISCONNECTED set, it is possible for it to get cleared
before the BUG_ON, and it is is not possible to lock against that.
There are a couple of problems here.
Firstly, the code doesn't match the comment. The comment describes
a 'disconnected' dentry as being IS_ROOT as well as DCACHE_DISCONNECTED,
however there is not testing of IS_ROOT anythere.
A dentry is marked DCACHE_DISCONNECTED when allocated with
d_alloc_anon, and remains DCACHE_DISCONNECTED while a path is built up
towards the root. So a dentry can have a valid name and a valid
parent and even grandparent, but will still be DCACHE_DISCONNECTED
until a path to the root is created. Once the path to the root is
complete, everything in the path gets DCACHE_DISCONNECTED cleared. So
the fact that DCACHE_DISCONNECTED isn't enough to say that a dentry is
free to be spliced in with a given name. This can only be allowed if
the dentry does not yet have a name, so the IS_ROOT test is needed too.
However even adding that test to __d_find_alias isn't enough. As
d_splice_alias drops dcache_lock before calling d_move to perform the
splice, it could race with another thread calling d_splice_alias to
splice the inode in with a different name in a different part of the
tree (in the case where a file has hard links). So that splicing code
is only really safe for directories (as we know that directories only
have one link). For directories, the caller of d_splice_alias will be
holding i_mutex on the (unique) parent so there is no room for a race.
A consequence of this is that a non-directory will never benefit from
being spliced into a pre-exisiting dentry, but that isn't a problem.
It is perfectly OK for a non-directory to have multiple dentries, some
anonymous, some not. And the comment for d_splice_alias says that it
only happens for directories anyway.
Signed-off-by: Neil Brown <neilb@suse.de>
### Diffstat output
./fs/dcache.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff .prev/fs/dcache.c ./fs/dcache.c
--- .prev/fs/dcache.c 2006-09-29 12:57:26.000000000 +1000
+++ ./fs/dcache.c 2006-09-29 12:57:34.000000000 +1000
@@ -291,9 +291,9 @@ struct dentry * dget_locked(struct dentr
* it can be unhashed only if it has no children, or if it is the root
* of a filesystem.
*
- * If the inode has a DCACHE_DISCONNECTED alias, then prefer
+ * If the inode has an IS_ROOT, DCACHE_DISCONNECTED alias, then prefer
* any other hashed alias over that one unless @want_discon is set,
- * in which case only return a DCACHE_DISCONNECTED alias.
+ * in which case only return an IS_ROOT, DCACHE_DISCONNECTED alias.
*/
static struct dentry * __d_find_alias(struct inode *inode, int want_discon)
@@ -309,7 +309,8 @@ static struct dentry * __d_find_alias(st
prefetch(next);
alias = list_entry(tmp, struct dentry, d_alias);
if (S_ISDIR(inode->i_mode) || !d_unhashed(alias)) {
- if (alias->d_flags & DCACHE_DISCONNECTED)
+ if (IS_ROOT(alias) &&
+ (alias->d_flags & DCACHE_DISCONNECTED))
discon_alias = alias;
else if (!want_discon) {
__dget_locked(alias);
@@ -1134,7 +1135,7 @@ struct dentry *d_splice_alias(struct ino
{
struct dentry *new = NULL;
- if (inode) {
+ if (inode && S_ISDIR(inode->i_mode)) {
spin_lock(&dcache_lock);
new = __d_find_alias(inode, 1);
if (new) {
next prev parent reply other threads:[~2006-09-29 3:09 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-09-29 3:08 [PATCH 000 of 8] knfsd: Introduction NeilBrown
2006-09-29 3:08 ` [PATCH 001 of 8] knfsd: Add nfs-export support to tmpfs NeilBrown
2006-09-29 6:29 ` Andrew Morton
2006-09-29 6:48 ` [NFS] " Neil Brown
2006-09-29 19:41 ` Hugh Dickins
2006-10-03 0:08 ` Neil Brown
2006-09-29 3:08 ` [PATCH 002 of 8] knfsd: lockd: fix refount on nsm NeilBrown
2006-09-29 6:01 ` [NFS] " Olaf Kirch
2006-09-29 3:08 ` [PATCH 003 of 8] knfsd: Fix auto-sizing of nfsd request/reply buffers NeilBrown
2006-09-29 3:08 ` NeilBrown [this message]
2006-09-29 3:09 ` [PATCH 005 of 8] knfsd: nfsd: store export path in export NeilBrown
2006-09-29 3:09 ` [PATCH 006 of 8] knfsd: nfsd4: fslocations data structures NeilBrown
2006-09-29 6:45 ` Andrew Morton
2006-10-02 18:23 ` [NFS] " J. Bruce Fields
2006-10-02 18:24 ` [PATCH 1 of 3] nfsd4: fix fs locations bounds-checking J. Bruce Fields
2006-10-02 18:26 ` [PATCH 2 of 3] nfsd4: fslocs: fix compile in non-CONFIG_NFSD_V4 case J. Bruce Fields
2006-10-02 18:26 ` [PATCH 3 of 3] nfsd4: fslocs: remove spurious NULL check J. Bruce Fields
2006-09-29 3:09 ` [PATCH 007 of 8] knfsd: nfsd4: xdr encoding for fs_locations NeilBrown
2006-09-29 3:09 ` [PATCH 008 of 8] knfsd: nfsd4: actually use all the pieces to implement referrals NeilBrown
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1060929030858.24062@suse.de \
--to=neilb@suse.de \
--cc=akpm@osdl.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nfs@lists.sourceforge.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox