UPSTREAM: fix races between __d_instantiate() and checks of dentry flags
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.
Cc: stable@vger.kernel.org # 3.13 and 3.14 need that one
Reported-and-tested-by: Lin Ming <minggr@gmail.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
(cherry picked from commit 22213318af)
BUG=chrome-os-partner:36188
TEST=None
Change-Id: I1b7f82c19eae6e8adb2ea05d03f232fddf277d2e
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Reviewed-on: https://chromium-review.googlesource.com/245198
Tested-by: 春梅 陸 <sun_lu@asus.com>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Trybot-Ready: Douglas Anderson <dianders@chromium.org>
Commit-Queue: Eddie Cai <eddie.cai@rock-chips.com>
Tested-by: Eddie Cai <eddie.cai@rock-chips.com>
This commit is contained in:
committed by
ChromeOS Commit Bot
parent
3ba6f85f18
commit
3aee2c3ce9
@@ -1647,8 +1647,7 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
|
||||
unsigned add_flags = d_flags_for_inode(inode);
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
dentry->d_flags &= ~DCACHE_ENTRY_TYPE;
|
||||
dentry->d_flags |= add_flags;
|
||||
__d_set_type(dentry, add_flags);
|
||||
if (inode)
|
||||
hlist_add_head(&dentry->d_alias, &inode->i_dentry);
|
||||
dentry->d_inode = inode;
|
||||
|
||||
@@ -1538,7 +1538,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)) {
|
||||
@@ -2241,7 +2241,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;
|
||||
@@ -2984,7 +2984,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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user