ovl: fix wrong use of impure dir cache in ovl_iterate()
authorAmir Goldstein <amir73il@gmail.com>
Tue, 17 Jul 2018 13:05:38 +0000 (16:05 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 9 Sep 2018 17:55:58 +0000 (19:55 +0200)
commit 67810693077afc1ebf9e1646af300436cb8103c2 upstream.

Only upper dir can be impure, but if we are in the middle of
iterating a lower real dir, dir could be copied up and marked
impure. We only want the impure cache if we started iterating
a real upper dir to begin with.

Aditya Kali reported that the following reproducer hits the
WARN_ON(!cache->refcount) in ovl_get_cache():

 docker run --rm drupal:8.5.4-fpm-alpine \
    sh -c 'cd /var/www/html/vendor/symfony && \
           chown -R www-data:www-data . && ls -l .'

Reported-by: Aditya Kali <adityakali@google.com>
Tested-by: Aditya Kali <adityakali@google.com>
Fixes: 4edb83bb1041 ('ovl: constant d_ino for non-merge dirs')
Cc: <stable@vger.kernel.org> # v4.14
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/overlayfs/readdir.c

index 7fa7d68baa6d197d81f868ab795d5316f236f006..1d4f9997236f221fbd72d8a42405e3730f73c612 100644 (file)
@@ -623,6 +623,21 @@ static int ovl_fill_real(struct dir_context *ctx, const char *name,
        return orig_ctx->actor(orig_ctx, name, namelen, offset, ino, d_type);
 }
 
+static bool ovl_is_impure_dir(struct file *file)
+{
+       struct ovl_dir_file *od = file->private_data;
+       struct inode *dir = d_inode(file->f_path.dentry);
+
+       /*
+        * Only upper dir can be impure, but if we are in the middle of
+        * iterating a lower real dir, dir could be copied up and marked
+        * impure. We only want the impure cache if we started iterating
+        * a real upper dir to begin with.
+        */
+       return od->is_upper && ovl_test_flag(OVL_IMPURE, dir);
+
+}
+
 static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
 {
        int err;
@@ -646,7 +661,7 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx)
                rdt.parent_ino = stat.ino;
        }
 
-       if (ovl_test_flag(OVL_IMPURE, d_inode(dir))) {
+       if (ovl_is_impure_dir(file)) {
                rdt.cache = ovl_cache_get_impure(&file->f_path);
                if (IS_ERR(rdt.cache))
                        return PTR_ERR(rdt.cache);
@@ -676,7 +691,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
                 * entries.
                 */
                if (ovl_same_sb(dentry->d_sb) &&
-                   (ovl_test_flag(OVL_IMPURE, d_inode(dentry)) ||
+                   (ovl_is_impure_dir(file) ||
                     OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent)))) {
                        return ovl_iterate_real(file, ctx);
                }