mcoverlayfs: fix NULL pointer dereference on ovl_dentry_release()

This commit is contained in:
Yoichi Umezawa
2017-03-28 21:52:41 +09:00
parent f2ab0193e5
commit 4ee0c05e08
3 changed files with 78 additions and 5 deletions

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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;