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);
|
dentry, dentry->d_inode->i_ino);
|
||||||
OVL_DEBUG("sysfs: realpath.dentry=%pd4, i_ino=%lu\n",
|
OVL_DEBUG("sysfs: realpath.dentry=%pd4, i_ino=%lu\n",
|
||||||
realpath.dentry, realpath.dentry->d_inode->i_ino);
|
realpath.dentry, realpath.dentry->d_inode->i_ino);
|
||||||
if (!dentry->d_inode->i_private) {
|
if (!ovl_find_d_fsdata(dentry)) {
|
||||||
dentry->d_inode->i_private = dentry->d_fsdata;
|
ovl_add_d_fsdata(dentry);
|
||||||
dentry->d_fsdata = realpath.dentry->d_fsdata;
|
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_NOCOPYUPW(opt) ((opt) & __OVL_OPT_NOCOPYUPW)
|
||||||
#define OVL_OPT_NOFSCHECK(opt) ((opt) & __OVL_OPT_NOFSCHECK)
|
#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)
|
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int err = vfs_rmdir(dir, 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);
|
unsigned ovl_get_config_opt(struct dentry *dentry);
|
||||||
void ovl_reset_ovl_entry(struct ovl_entry **oe, 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);
|
enum ovl_path_type ovl_path_type(struct dentry *dentry);
|
||||||
u64 ovl_dentry_version_get(struct dentry *dentry);
|
u64 ovl_dentry_version_get(struct dentry *dentry);
|
||||||
void ovl_dentry_version_inc(struct dentry *dentry);
|
void ovl_dentry_version_inc(struct dentry *dentry);
|
||||||
|
|||||||
@ -45,6 +45,7 @@ struct ovl_fs {
|
|||||||
long lower_namelen;
|
long lower_namelen;
|
||||||
/* pathnames of lower and upper dirs, for show_options */
|
/* pathnames of lower and upper dirs, for show_options */
|
||||||
struct ovl_config config;
|
struct ovl_config config;
|
||||||
|
struct list_head d_fsdata_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ovl_dir_cache;
|
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)
|
void ovl_reset_ovl_entry(struct ovl_entry **oe, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
unsigned opt = ovl_get_config_opt(dentry);
|
unsigned opt = ovl_get_config_opt(dentry);
|
||||||
|
struct ovl_entry *d_fsdata;
|
||||||
|
|
||||||
if (OVL_OPT_NOFSCHECK(opt)) {
|
if (OVL_OPT_NOFSCHECK(opt)) {
|
||||||
if (dentry->d_inode && dentry->d_inode->i_private &&
|
if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
|
||||||
!S_ISDIR(dentry->d_inode->i_mode)) {
|
return;
|
||||||
*oe = dentry->d_inode->i_private;
|
}
|
||||||
|
|
||||||
|
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)
|
static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
|
||||||
{
|
{
|
||||||
return oe->numlower ? oe->lowerstack[0].dentry : NULL;
|
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;
|
struct ovl_fs *ufs = sb->s_fs_info;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
|
ovl_clear_d_fsdata(ufs);
|
||||||
|
|
||||||
dput(ufs->workdir);
|
dput(ufs->workdir);
|
||||||
mntput(ufs->upper_mnt);
|
mntput(ufs->upper_mnt);
|
||||||
for (i = 0; i < ufs->numlower; i++)
|
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)
|
if (!ufs)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&ufs->d_fsdata_list);
|
||||||
err = ovl_parse_opt((char *) data, &ufs->config);
|
err = ovl_parse_opt((char *) data, &ufs->config);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_free_config;
|
goto out_free_config;
|
||||||
|
|||||||
Reference in New Issue
Block a user