GFS2: Add kobject release method
This patch adds a kobject release function that properly maintains the kobject use count, so that accesses to the sysfs files do not cause an access to freed kernel memory after an unmount. Signed-off-by: Bob Peterson <rpeterso@redhat.com> Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
This commit is contained in:
parent
0fe2f1e929
commit
0d515210b6
@ -1118,20 +1118,33 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
|
|||||||
}
|
}
|
||||||
|
|
||||||
error = init_names(sdp, silent);
|
error = init_names(sdp, silent);
|
||||||
if (error)
|
if (error) {
|
||||||
goto fail;
|
/* In this case, we haven't initialized sysfs, so we have to
|
||||||
|
manually free the sdp. */
|
||||||
|
free_percpu(sdp->sd_lkstats);
|
||||||
|
kfree(sdp);
|
||||||
|
sb->s_fs_info = NULL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s", sdp->sd_table_name);
|
snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s", sdp->sd_table_name);
|
||||||
|
|
||||||
gfs2_create_debugfs_file(sdp);
|
|
||||||
|
|
||||||
error = gfs2_sys_fs_add(sdp);
|
error = gfs2_sys_fs_add(sdp);
|
||||||
|
/*
|
||||||
|
* If we hit an error here, gfs2_sys_fs_add will have called function
|
||||||
|
* kobject_put which causes the sysfs usage count to go to zero, which
|
||||||
|
* causes sysfs to call function gfs2_sbd_release, which frees sdp.
|
||||||
|
* Subsequent error paths here will call gfs2_sys_fs_del, which also
|
||||||
|
* kobject_put to free sdp.
|
||||||
|
*/
|
||||||
if (error)
|
if (error)
|
||||||
goto fail;
|
return error;
|
||||||
|
|
||||||
|
gfs2_create_debugfs_file(sdp);
|
||||||
|
|
||||||
error = gfs2_lm_mount(sdp, silent);
|
error = gfs2_lm_mount(sdp, silent);
|
||||||
if (error)
|
if (error)
|
||||||
goto fail_sys;
|
goto fail_debug;
|
||||||
|
|
||||||
error = init_locking(sdp, &mount_gh, DO);
|
error = init_locking(sdp, &mount_gh, DO);
|
||||||
if (error)
|
if (error)
|
||||||
@ -1215,12 +1228,12 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
|
|||||||
fail_lm:
|
fail_lm:
|
||||||
gfs2_gl_hash_clear(sdp);
|
gfs2_gl_hash_clear(sdp);
|
||||||
gfs2_lm_unmount(sdp);
|
gfs2_lm_unmount(sdp);
|
||||||
fail_sys:
|
fail_debug:
|
||||||
gfs2_sys_fs_del(sdp);
|
|
||||||
fail:
|
|
||||||
gfs2_delete_debugfs_file(sdp);
|
gfs2_delete_debugfs_file(sdp);
|
||||||
free_percpu(sdp->sd_lkstats);
|
free_percpu(sdp->sd_lkstats);
|
||||||
kfree(sdp);
|
/* gfs2_sys_fs_del must be the last thing we do, since it causes
|
||||||
|
* sysfs to call function gfs2_sbd_release, which frees sdp. */
|
||||||
|
gfs2_sys_fs_del(sdp);
|
||||||
sb->s_fs_info = NULL;
|
sb->s_fs_info = NULL;
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -1390,10 +1403,9 @@ static void gfs2_kill_sb(struct super_block *sb)
|
|||||||
sdp->sd_root_dir = NULL;
|
sdp->sd_root_dir = NULL;
|
||||||
sdp->sd_master_dir = NULL;
|
sdp->sd_master_dir = NULL;
|
||||||
shrink_dcache_sb(sb);
|
shrink_dcache_sb(sb);
|
||||||
kill_block_super(sb);
|
|
||||||
gfs2_delete_debugfs_file(sdp);
|
gfs2_delete_debugfs_file(sdp);
|
||||||
free_percpu(sdp->sd_lkstats);
|
free_percpu(sdp->sd_lkstats);
|
||||||
kfree(sdp);
|
kill_block_super(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct file_system_type gfs2_fs_type = {
|
struct file_system_type gfs2_fs_type = {
|
||||||
|
@ -276,7 +276,15 @@ static struct attribute *gfs2_attrs[] = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void gfs2_sbd_release(struct kobject *kobj)
|
||||||
|
{
|
||||||
|
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
|
||||||
|
|
||||||
|
kfree(sdp);
|
||||||
|
}
|
||||||
|
|
||||||
static struct kobj_type gfs2_ktype = {
|
static struct kobj_type gfs2_ktype = {
|
||||||
|
.release = gfs2_sbd_release,
|
||||||
.default_attrs = gfs2_attrs,
|
.default_attrs = gfs2_attrs,
|
||||||
.sysfs_ops = &gfs2_attr_ops,
|
.sysfs_ops = &gfs2_attr_ops,
|
||||||
};
|
};
|
||||||
@ -583,6 +591,7 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
|
|||||||
char ro[20];
|
char ro[20];
|
||||||
char spectator[20];
|
char spectator[20];
|
||||||
char *envp[] = { ro, spectator, NULL };
|
char *envp[] = { ro, spectator, NULL };
|
||||||
|
int sysfs_frees_sdp = 0;
|
||||||
|
|
||||||
sprintf(ro, "RDONLY=%d", (sb->s_flags & MS_RDONLY) ? 1 : 0);
|
sprintf(ro, "RDONLY=%d", (sb->s_flags & MS_RDONLY) ? 1 : 0);
|
||||||
sprintf(spectator, "SPECTATOR=%d", sdp->sd_args.ar_spectator ? 1 : 0);
|
sprintf(spectator, "SPECTATOR=%d", sdp->sd_args.ar_spectator ? 1 : 0);
|
||||||
@ -591,8 +600,10 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
|
|||||||
error = kobject_init_and_add(&sdp->sd_kobj, &gfs2_ktype, NULL,
|
error = kobject_init_and_add(&sdp->sd_kobj, &gfs2_ktype, NULL,
|
||||||
"%s", sdp->sd_table_name);
|
"%s", sdp->sd_table_name);
|
||||||
if (error)
|
if (error)
|
||||||
goto fail;
|
goto fail_reg;
|
||||||
|
|
||||||
|
sysfs_frees_sdp = 1; /* Freeing sdp is now done by sysfs calling
|
||||||
|
function gfs2_sbd_release. */
|
||||||
error = sysfs_create_group(&sdp->sd_kobj, &tune_group);
|
error = sysfs_create_group(&sdp->sd_kobj, &tune_group);
|
||||||
if (error)
|
if (error)
|
||||||
goto fail_reg;
|
goto fail_reg;
|
||||||
@ -615,9 +626,13 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
|
|||||||
fail_tune:
|
fail_tune:
|
||||||
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
|
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
|
||||||
fail_reg:
|
fail_reg:
|
||||||
kobject_put(&sdp->sd_kobj);
|
free_percpu(sdp->sd_lkstats);
|
||||||
fail:
|
|
||||||
fs_err(sdp, "error %d adding sysfs files", error);
|
fs_err(sdp, "error %d adding sysfs files", error);
|
||||||
|
if (sysfs_frees_sdp)
|
||||||
|
kobject_put(&sdp->sd_kobj);
|
||||||
|
else
|
||||||
|
kfree(sdp);
|
||||||
|
sb->s_fs_info = NULL;
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user