affs_lookup(): close a race with affs_remove_link()
authorAl Viro <viro@zeniv.linux.org.uk>
Sun, 6 May 2018 16:15:20 +0000 (12:15 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 May 2018 05:51:47 +0000 (07:51 +0200)
commit 30da870ce4a4e007c901858a96e9e394a1daa74a upstream.

we unlock the directory hash too early - if we are looking at secondary
link and primary (in another directory) gets removed just as we unlock,
we could have the old primary moved in place of the secondary, leaving
us to look into freed entry (and leaving our dentry with ->d_fsdata
pointing to a freed entry).

Cc: stable@vger.kernel.org # 2.4.4+
Acked-by: David Sterba <dsterba@suse.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/affs/namei.c

index d8aa0ae3d037c8b91ab13f7d3f6d209bd2326797..1ed0fa4c4d481842c22ac4a3a95f5b867c84f53d 100644 (file)
@@ -206,9 +206,10 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
 
        affs_lock_dir(dir);
        bh = affs_find_entry(dir, dentry);
-       affs_unlock_dir(dir);
-       if (IS_ERR(bh))
+       if (IS_ERR(bh)) {
+               affs_unlock_dir(dir);
                return ERR_CAST(bh);
+       }
        if (bh) {
                u32 ino = bh->b_blocknr;
 
@@ -222,10 +223,13 @@ affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
                }
                affs_brelse(bh);
                inode = affs_iget(sb, ino);
-               if (IS_ERR(inode))
+               if (IS_ERR(inode)) {
+                       affs_unlock_dir(dir);
                        return ERR_CAST(inode);
+               }
        }
        d_add(dentry, inode);
+       affs_unlock_dir(dir);
        return NULL;
 }