diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2014-04-19 12:30:58 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-06-07 10:28:20 -0700 |
commit | f0754e5903c14a1f36da90275b1c17becddc581c (patch) | |
tree | 83d69ce8108e0aca281da55f17c14bbecca9facf /fs/namei.c | |
parent | 36eca0cf9c5f995fd0b7933a11d58fa5f640f3ae (diff) | |
download | lwn-f0754e5903c14a1f36da90275b1c17becddc581c.tar.gz lwn-f0754e5903c14a1f36da90275b1c17becddc581c.zip |
fix races between __d_instantiate() and checks of dentry flags
commit 22213318af7ae265bc6cd8aef2febbc2d69a2440 upstream.
in non-lazy walk we need to be careful about dentry switching from
negative to positive - both ->d_flags and ->d_inode are updated,
and in some places we might see only one store. The cases where
dentry has been obtained by dcache lookup with ->i_mutex held on
parent are safe - ->d_lock and ->i_mutex provide all the barriers
we need. However, there are several places where we run into
trouble:
* do_last() fetches ->d_inode, then checks ->d_flags and
assumes that inode won't be NULL unless d_is_negative() is true.
Race with e.g. creat() - we might have fetched the old value of
->d_inode (still NULL) and new value of ->d_flags (already not
DCACHE_MISS_TYPE). Lin Ming has observed and reported the resulting
oops.
* a bunch of places checks ->d_inode for being non-NULL,
then checks ->d_flags for "is it a symlink". Race with symlink(2)
in case if our CPU sees ->d_inode update first - we see non-NULL
there, but ->d_flags still contains DCACHE_MISS_TYPE instead of
DCACHE_SYMLINK_TYPE. Result: false negative on "should we follow
link here?", with subsequent unpleasantness.
Reported-and-tested-by: Lin Ming <minggr@gmail.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 6 |
1 files changed, 3 insertions, 3 deletions
diff --git a/fs/namei.c b/fs/namei.c index 4b491b431990..4a3c105cf703 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1537,7 +1537,7 @@ static inline int walk_component(struct nameidata *nd, struct path *path, inode = path->dentry->d_inode; } err = -ENOENT; - if (!inode) + if (!inode || d_is_negative(path->dentry)) goto out_path_put; if (should_follow_link(path->dentry, follow)) { @@ -2240,7 +2240,7 @@ mountpoint_last(struct nameidata *nd, struct path *path) mutex_unlock(&dir->d_inode->i_mutex); done: - if (!dentry->d_inode) { + if (!dentry->d_inode || d_is_negative(dentry)) { error = -ENOENT; dput(dentry); goto out; @@ -2982,7 +2982,7 @@ retry_lookup: finish_lookup: /* we _can_ be in RCU mode here */ error = -ENOENT; - if (d_is_negative(path->dentry)) { + if (!inode || d_is_negative(path->dentry)) { path_to_nameidata(path, nd); goto out; } |