fix races between __d_instantiate() and checks of dentry flags
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 19 Apr 2014 16:30:58 +0000 (12:30 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 7 Jun 2014 17:28:20 +0000 (10:28 -0700)
commitf0754e5903c14a1f36da90275b1c17becddc581c
tree83d69ce8108e0aca281da55f17c14bbecca9facf
parent36eca0cf9c5f995fd0b7933a11d58fa5f640f3ae
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>
fs/dcache.c
fs/namei.c