]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
NFS: fix two problems in lookup_revalidate in RCU-walk
authorNeilBrown <neilb@suse.de>
Mon, 4 Aug 2014 06:24:00 +0000 (16:24 +1000)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Mon, 4 Aug 2014 13:22:08 +0000 (09:22 -0400)
1/ rcu_dereference isn't correct: that field isn't
   RCU protected.   It could potentially change at any time
   so ACCESS_ONCE might be justified.

   changes to ->d_parent are protected by ->d_seq.  However
   that isn't always checked after ->d_revalidate is called,
   so it is safest to keep the double-check that ->d_parent
   hasn't changed at the end of these functions.

2/ in nfs4_lookup_revalidate, "->d_parent" was forgotten.
   So 'parent' was not the parent of 'dentry'.
   This fails safe is the context is that dentry->d_inode is
   NULL, and the result of parent->d_inode being NULL is
   that ECHILD is returned, which is always safe.

Reported-by: kbuild test robot <fengguang.wu@intel.com>
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
fs/nfs/dir.c

index 1b5f38f48dab6aba4ba5f7e3713785575efc9fec..36d921f0c6026c27170b565f46eb4e26999ea812 100644 (file)
@@ -1102,7 +1102,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
        int error;
 
        if (flags & LOOKUP_RCU) {
-               parent = rcu_dereference(dentry->d_parent);
+               parent = ACCESS_ONCE(dentry->d_parent);
                dir = ACCESS_ONCE(parent->d_inode);
                if (!dir)
                        return -ECHILD;
@@ -1184,7 +1184,7 @@ out_set_verifier:
        nfs_advise_use_readdirplus(dir);
  out_valid_noent:
        if (flags & LOOKUP_RCU) {
-               if (parent != rcu_dereference(dentry->d_parent))
+               if (parent != ACCESS_ONCE(dentry->d_parent))
                        return -ECHILD;
        } else
                dput(parent);
@@ -1585,7 +1585,7 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
                struct inode *dir;
 
                if (flags & LOOKUP_RCU) {
-                       parent = rcu_dereference(dentry);
+                       parent = ACCESS_ONCE(dentry->d_parent);
                        dir = ACCESS_ONCE(parent->d_inode);
                        if (!dir)
                                return -ECHILD;
@@ -1599,7 +1599,7 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
                        ret = -ECHILD;
                if (!(flags & LOOKUP_RCU))
                        dput(parent);
-               else if (parent != rcu_dereference(dentry))
+               else if (parent != ACCESS_ONCE(dentry->d_parent))
                        return -ECHILD;
                goto out;
        }