mcoverlayfs: fix NULL pointer dereference on ovl_dentry_release()
This commit is contained in:
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user