From 4ee0c05e08a242a17a8bcd41de53bf2ee5c37892 Mon Sep 17 00:00:00 2001 From: Yoichi Umezawa Date: Tue, 28 Mar 2017 21:52:41 +0900 Subject: [PATCH] mcoverlayfs: fix NULL pointer dereference on ovl_dentry_release() --- .../kernel/mcoverlayfs/linux-4.6.7/inode.c | 4 +- .../mcoverlayfs/linux-4.6.7/overlayfs.h | 8 +++ .../kernel/mcoverlayfs/linux-4.6.7/super.c | 71 ++++++++++++++++++- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/executer/kernel/mcoverlayfs/linux-4.6.7/inode.c b/executer/kernel/mcoverlayfs/linux-4.6.7/inode.c index 1950006b..03bb4dfc 100644 --- a/executer/kernel/mcoverlayfs/linux-4.6.7/inode.c +++ b/executer/kernel/mcoverlayfs/linux-4.6.7/inode.c @@ -420,8 +420,8 @@ struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) dentry, dentry->d_inode->i_ino); OVL_DEBUG("sysfs: realpath.dentry=%pd4, i_ino=%lu\n", realpath.dentry, realpath.dentry->d_inode->i_ino); - if (!dentry->d_inode->i_private) { - dentry->d_inode->i_private = dentry->d_fsdata; + if (!ovl_find_d_fsdata(dentry)) { + ovl_add_d_fsdata(dentry); dentry->d_fsdata = realpath.dentry->d_fsdata; } } diff --git a/executer/kernel/mcoverlayfs/linux-4.6.7/overlayfs.h b/executer/kernel/mcoverlayfs/linux-4.6.7/overlayfs.h index 82d24c34..f3d6fbc6 100644 --- a/executer/kernel/mcoverlayfs/linux-4.6.7/overlayfs.h +++ b/executer/kernel/mcoverlayfs/linux-4.6.7/overlayfs.h @@ -43,6 +43,12 @@ enum ovl_opt_bit { #define OVL_OPT_NOCOPYUPW(opt) ((opt) & __OVL_OPT_NOCOPYUPW) #define OVL_OPT_NOFSCHECK(opt) ((opt) & __OVL_OPT_NOFSCHECK) +struct ovl_d_fsdata { + struct list_head list; + struct dentry *d; + struct ovl_entry *oe; +}; + static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) { int err = vfs_rmdir(dir, dentry); @@ -149,6 +155,8 @@ static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry) unsigned ovl_get_config_opt(struct dentry *dentry); void ovl_reset_ovl_entry(struct ovl_entry **oe, struct dentry *dentry); +struct ovl_entry *ovl_find_d_fsdata(struct dentry *dentry); +int ovl_add_d_fsdata(struct dentry *dentry); enum ovl_path_type ovl_path_type(struct dentry *dentry); u64 ovl_dentry_version_get(struct dentry *dentry); void ovl_dentry_version_inc(struct dentry *dentry); diff --git a/executer/kernel/mcoverlayfs/linux-4.6.7/super.c b/executer/kernel/mcoverlayfs/linux-4.6.7/super.c index 565dde30..3061333b 100644 --- a/executer/kernel/mcoverlayfs/linux-4.6.7/super.c +++ b/executer/kernel/mcoverlayfs/linux-4.6.7/super.c @@ -45,6 +45,7 @@ struct ovl_fs { long lower_namelen; /* pathnames of lower and upper dirs, for show_options */ struct ovl_config config; + struct list_head d_fsdata_list; }; struct ovl_dir_cache; @@ -76,15 +77,76 @@ unsigned ovl_get_config_opt(struct dentry *dentry) void ovl_reset_ovl_entry(struct ovl_entry **oe, struct dentry *dentry) { unsigned opt = ovl_get_config_opt(dentry); + struct ovl_entry *d_fsdata; if (OVL_OPT_NOFSCHECK(opt)) { - if (dentry->d_inode && dentry->d_inode->i_private && - !S_ISDIR(dentry->d_inode->i_mode)) { - *oe = dentry->d_inode->i_private; + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { + return; + } + + d_fsdata = ovl_find_d_fsdata(dentry); + if (d_fsdata) { + OVL_DEBUG("reset: dentry=%pd4, 0x%p, oe=0x%p\n", + dentry, dentry, d_fsdata); + *oe = d_fsdata; } } } +struct ovl_entry *ovl_find_d_fsdata(struct dentry *dentry) +{ + struct ovl_fs *ofs = dentry->d_sb->s_fs_info; + struct ovl_d_fsdata *d_fsdata; + + list_for_each_entry(d_fsdata, &ofs->d_fsdata_list, list) { + if (dentry == d_fsdata->d) { + OVL_DEBUG("exist: dentry=%pd4, 0x%p, oe=0x%p\n", + d_fsdata->d, d_fsdata->d, d_fsdata->oe); + return d_fsdata->oe; + } + } + + return NULL; +} + +int ovl_add_d_fsdata(struct dentry *dentry) +{ + struct ovl_fs *ofs = dentry->d_sb->s_fs_info; + struct ovl_d_fsdata *d_fsdata; + + d_fsdata = kzalloc(sizeof(struct ovl_d_fsdata), GFP_KERNEL); + if (!d_fsdata) { + return -1; + } + + d_fsdata->d = dentry; + d_fsdata->oe = dentry->d_fsdata; + + list_add(&d_fsdata->list, &ofs->d_fsdata_list); + + OVL_DEBUG("add: dentry=%pd4, 0x%p, oe=0x%p\n", + d_fsdata->d, d_fsdata->d, d_fsdata->oe); + + return 0; +} + +static int ovl_clear_d_fsdata(struct ovl_fs *ofs) +{ + struct ovl_d_fsdata *d_fsdata; + struct ovl_d_fsdata *d_fsdata_next; + + list_for_each_entry_safe(d_fsdata, d_fsdata_next, &ofs->d_fsdata_list, + list) { + OVL_DEBUG("delete: dentry=%pd4, 0x%p\n", + d_fsdata->d, d_fsdata->d); + list_del(&d_fsdata->list); + + kfree(d_fsdata); + } + + return 0; +} + static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe) { return oe->numlower ? oe->lowerstack[0].dentry : NULL; @@ -658,6 +720,8 @@ static void ovl_put_super(struct super_block *sb) struct ovl_fs *ufs = sb->s_fs_info; unsigned i; + ovl_clear_d_fsdata(ufs); + dput(ufs->workdir); mntput(ufs->upper_mnt); for (i = 0; i < ufs->numlower; i++) @@ -1049,6 +1113,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!ufs) goto out; + INIT_LIST_HEAD(&ufs->d_fsdata_list); err = ovl_parse_opt((char *) data, &ufs->config); if (err) goto out_free_config;