diff options
author | Nick Piggin <npiggin@kernel.dk> | 2011-01-14 02:36:19 +0000 |
---|---|---|
committer | Nick Piggin <npiggin@hera.kernel.org> | 2011-01-14 02:36:19 +0000 |
commit | 90dbb77ba48dddb87445d238e84cd137cf97dd98 (patch) | |
tree | 446772602e5944075a6c614db05dd06681f3f3d8 /fs | |
parent | bb20c18db6fbb5e6ba499c76473a487d35073467 (diff) |
fs: fix dropping of rcu-walk from force_reval_path
As J. R. Okajima noted, force_reval_path passes in the same dentry to
d_revalidate as the one in the nameidata structure (other callers pass in a
child), so the locking breaks. This can oops with a chrooted nfs mount, for
example. Similarly there can be other problems with revalidating a dentry
which is already in nameidata of the path walk.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/fs/namei.c b/fs/namei.c index 0f02359ce685..14c73edca9ce 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -479,6 +479,14 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry struct fs_struct *fs = current->fs; struct dentry *parent = nd->path.dentry; + /* + * It can be possible to revalidate the dentry that we started + * the path walk with. force_reval_path may also revalidate the + * dentry already committed to the nameidata. + */ + if (unlikely(parent == dentry)) + return nameidata_drop_rcu(nd); + BUG_ON(!(nd->flags & LOOKUP_RCU)); if (nd->root.mnt) { spin_lock(&fs->lock); |