Kdump kernel fails to load because of crash on mount of overlayfs:

 BUG: unable to handle kernel NULL pointer dereference at 0000000000000060
....
 Call Trace:
  seq_path+0x64/0xb0
  print_paths_option+0x79/0xa0
  ovl_show_options+0x3a/0x320
  show_mountinfo+0x1ee/0x290
  seq_read+0x2f8/0x400
  vfs_read+0x9d/0x150
  ksys_read+0x4f/0xb0
  do_syscall_64+0x5b/0x1a0

This is cause by OOB access of ofs->lowerpaths.
We transfer to print_paths_option() ofs->numlayer as size of ->lowerpaths
array, but it's not. We could probably pass 'ofs->numlayer - 1' as
number of lower layers/path, but it's better to remove lowerpaths
completely. All necessary information already contained in 'struct ovl_entry'.
Use it to print paths instead.

Fixes: 17fc61697f73 ("overlayfs: add dynamic path resolving in mount options")
Fixes: 2191d729083d ("overlayfs: add mnt_id paths options")

https://jira.sw.ru/browse/PSBM-123508
Signed-off-by: Andrey Ryabinin <aryabi...@virtuozzo.com>
---
 fs/overlayfs/overlayfs.h |  4 ++--
 fs/overlayfs/ovl_entry.h |  1 -
 fs/overlayfs/super.c     | 30 ++++++++++++++----------------
 fs/overlayfs/util.c      | 13 +++++++++----
 4 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 7e103d002819..a708ebbd2e21 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -313,10 +313,10 @@ ssize_t ovl_getxattr(struct dentry *dentry, char *name, 
char **value,
 
 void print_path_option(struct seq_file *m, const char *name, struct path 
*path);
 void print_paths_option(struct seq_file *m, const char *name,
-                       struct path *paths, unsigned int num);
+                       struct ovl_path *paths, unsigned int num);
 void print_mnt_id_option(struct seq_file *m, const char *name, struct path 
*path);
 void print_mnt_ids_option(struct seq_file *m, const char *name,
-                       struct path *paths, unsigned int num);
+                       struct ovl_path *paths, unsigned int num);
 
 static inline bool ovl_is_impuredir(struct dentry *dentry)
 {
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index ea1906448ec5..4e7272c7e4dd 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -54,7 +54,6 @@ struct ovl_fs {
        unsigned int numlayer;
        /* Number of unique fs among layers including upper fs */
        unsigned int numfs;
-       struct path *lowerpaths;
        const struct ovl_layer *layers;
        struct ovl_sb *fs;
        /* workbasepath is the path at workdir= mount option */
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 3755f280036f..069d365a609d 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -241,11 +241,6 @@ static void ovl_free_fs(struct ovl_fs *ofs)
                ovl_inuse_unlock(ofs->upper_mnt->mnt_root);
        mntput(ofs->upper_mnt);
        path_put(&ofs->upperpath);
-       if (ofs->lowerpaths) {
-               for (i = 0; i < ofs->numlayer; i++)
-                       path_put(&ofs->lowerpaths[i]);
-               kfree(ofs->lowerpaths);
-       }
        for (i = 1; i < ofs->numlayer; i++) {
                iput(ofs->layers[i].trap);
                mntput(ofs->layers[i].mnt);
@@ -359,9 +354,10 @@ static int ovl_show_options(struct seq_file *m, struct 
dentry *dentry)
 {
        struct super_block *sb = dentry->d_sb;
        struct ovl_fs *ofs = sb->s_fs_info;
+       struct ovl_entry *oe = OVL_E(dentry);
 
        if (ovl_dyn_path_opts) {
-               print_paths_option(m, "lowerdir", ofs->lowerpaths, 
ofs->numlayer);
+               print_paths_option(m, "lowerdir", oe->lowerstack, oe->numlower);
                if (ofs->config.upperdir) {
                        print_path_option(m, "upperdir", &ofs->upperpath);
                        print_path_option(m, "workdir", &ofs->workbasepath);
@@ -375,7 +371,8 @@ static int ovl_show_options(struct seq_file *m, struct 
dentry *dentry)
        }
 
        if (ovl_mnt_id_path_opts) {
-               print_mnt_ids_option(m, "lowerdir_mnt_id", ofs->lowerpaths, 
ofs->numlayer);
+               print_mnt_ids_option(m, "lowerdir_mnt_id", oe->lowerstack, 
oe->numlower);
+
                /*
                 * We don't need to show mnt_id for workdir because it
                 * on the same mount as upperdir.
@@ -1625,6 +1622,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct 
super_block *sb,
 {
        int err;
        char *lowertmp, *lower;
+       struct path *stack = NULL;
        unsigned int stacklen, numlower = 0, i;
        struct ovl_entry *oe;
 
@@ -1649,14 +1647,14 @@ static struct ovl_entry *ovl_get_lowerstack(struct 
super_block *sb,
        }
 
        err = -ENOMEM;
-       ofs->lowerpaths = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
-       if (!ofs->lowerpaths)
+       stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
+       if (!stack)
                goto out_err;
 
        err = -EINVAL;
        lower = lowertmp;
        for (numlower = 0; numlower < stacklen; numlower++) {
-               err = ovl_lower_dir(lower, &ofs->lowerpaths[numlower], ofs,
+               err = ovl_lower_dir(lower, &stack[numlower], ofs,
                                    &sb->s_stack_depth);
                if (err)
                        goto out_err;
@@ -1671,7 +1669,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct 
super_block *sb,
                goto out_err;
        }
 
-       err = ovl_get_layers(sb, ofs, ofs->lowerpaths, numlower);
+       err = ovl_get_layers(sb, ofs, stack, numlower);
        if (err)
                goto out_err;
 
@@ -1681,20 +1679,20 @@ static struct ovl_entry *ovl_get_lowerstack(struct 
super_block *sb,
                goto out_err;
 
        for (i = 0; i < numlower; i++) {
-               oe->lowerstack[i].dentry = dget(ofs->lowerpaths[i].dentry);
+               oe->lowerstack[i].dentry = dget(stack[i].dentry);
                oe->lowerstack[i].layer = &ofs->layers[i+1];
        }
 
 out:
+       for (i = 0; i < numlower; i++)
+               path_put(&stack[i]);
+       kfree(stack);
+
        kfree(lowertmp);
 
        return oe;
 
 out_err:
-       for (i = 0; i < numlower; i++)
-               path_put_init(&ofs->lowerpaths[i]);
-       kfree(ofs->lowerpaths);
-       ofs->lowerpaths = NULL;
        oe = ERR_PTR(err);
        goto out;
 }
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 00f6c1483691..326e308039fb 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -933,16 +933,21 @@ void print_path_option(struct seq_file *m, const char 
*name, struct path *path)
 }
 
 void print_paths_option(struct seq_file *m, const char *name,
-                       struct path *paths, unsigned int num)
+                       struct ovl_path *paths, unsigned int num)
 {
        int i;
 
        seq_show_option(m, name, "");
 
        for (i = 0; i < num; i++) {
+               struct path tmp_path;
+
+               tmp_path.mnt = paths[i].layer->mnt;
+               tmp_path.dentry = paths[i].dentry;
+
                if (i)
                        seq_putc(m, ':');
-               seq_path(m, &paths[i], ", \t\n\\");
+               seq_path(m, &tmp_path, ", \t\n\\");
        }
 }
 
@@ -953,7 +958,7 @@ void print_mnt_id_option(struct seq_file *m, const char 
*name, struct path *path
 }
 
 void print_mnt_ids_option(struct seq_file *m, const char *name,
-                       struct path *paths, unsigned int num)
+                       struct ovl_path *paths, unsigned int num)
 {
        int i;
 
@@ -962,6 +967,6 @@ void print_mnt_ids_option(struct seq_file *m, const char 
*name,
        for (i = 0; i < num; i++) {
                if (i)
                        seq_putc(m, ':');
-               seq_printf(m, "%i", real_mount(paths[i].mnt)->mnt_id);
+               seq_printf(m, "%i", real_mount(paths[i].layer->mnt)->mnt_id);
        }
 }
-- 
2.26.2

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

Reply via email to