diff options
| author | Bob Peterson <rpeterso@redhat.com> | 2012-06-13 10:27:41 -0400 |
|---|---|---|
| committer | Steven Whitehouse <swhiteho@redhat.com> | 2012-06-13 10:59:48 -0400 |
| commit | 0d515210b6969ecfc161f71a4515831d9a6e58f4 (patch) | |
| tree | 2e5a5da49dae5d8c036d56fc7ae14532044dfac3 | |
| parent | 0fe2f1e929ecabf834f4af2ffd300fe70700f4b3 (diff) | |
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>
| -rw-r--r-- | fs/gfs2/ops_fstype.c | 36 | ||||
| -rw-r--r-- | fs/gfs2/sys.c | 21 |
2 files changed, 42 insertions, 15 deletions
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index b8c250fc4922..9b2389756acd 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c | |||
| @@ -1118,20 +1118,33 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent | |||
| 1118 | } | 1118 | } |
| 1119 | 1119 | ||
| 1120 | error = init_names(sdp, silent); | 1120 | error = init_names(sdp, silent); |
| 1121 | if (error) | 1121 | if (error) { |
| 1122 | goto fail; | 1122 | /* In this case, we haven't initialized sysfs, so we have to |
| 1123 | manually free the sdp. */ | ||
| 1124 | free_percpu(sdp->sd_lkstats); | ||
| 1125 | kfree(sdp); | ||
| 1126 | sb->s_fs_info = NULL; | ||
| 1127 | return error; | ||
| 1128 | } | ||
| 1123 | 1129 | ||
| 1124 | snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s", sdp->sd_table_name); | 1130 | snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s", sdp->sd_table_name); |
| 1125 | 1131 | ||
| 1126 | gfs2_create_debugfs_file(sdp); | ||
| 1127 | |||
| 1128 | error = gfs2_sys_fs_add(sdp); | 1132 | error = gfs2_sys_fs_add(sdp); |
| 1133 | /* | ||
| 1134 | * If we hit an error here, gfs2_sys_fs_add will have called function | ||
| 1135 | * kobject_put which causes the sysfs usage count to go to zero, which | ||
| 1136 | * causes sysfs to call function gfs2_sbd_release, which frees sdp. | ||
| 1137 | * Subsequent error paths here will call gfs2_sys_fs_del, which also | ||
| 1138 | * kobject_put to free sdp. | ||
| 1139 | */ | ||
| 1129 | if (error) | 1140 | if (error) |
| 1130 | goto fail; | 1141 | return error; |
| 1142 | |||
| 1143 | gfs2_create_debugfs_file(sdp); | ||
| 1131 | 1144 | ||
| 1132 | error = gfs2_lm_mount(sdp, silent); | 1145 | error = gfs2_lm_mount(sdp, silent); |
| 1133 | if (error) | 1146 | if (error) |
| 1134 | goto fail_sys; | 1147 | goto fail_debug; |
| 1135 | 1148 | ||
| 1136 | error = init_locking(sdp, &mount_gh, DO); | 1149 | error = init_locking(sdp, &mount_gh, DO); |
| 1137 | if (error) | 1150 | if (error) |
| @@ -1215,12 +1228,12 @@ fail_locking: | |||
| 1215 | fail_lm: | 1228 | fail_lm: |
| 1216 | gfs2_gl_hash_clear(sdp); | 1229 | gfs2_gl_hash_clear(sdp); |
| 1217 | gfs2_lm_unmount(sdp); | 1230 | gfs2_lm_unmount(sdp); |
| 1218 | fail_sys: | 1231 | fail_debug: |
| 1219 | gfs2_sys_fs_del(sdp); | ||
| 1220 | fail: | ||
| 1221 | gfs2_delete_debugfs_file(sdp); | 1232 | gfs2_delete_debugfs_file(sdp); |
| 1222 | free_percpu(sdp->sd_lkstats); | 1233 | free_percpu(sdp->sd_lkstats); |
| 1223 | kfree(sdp); | 1234 | /* gfs2_sys_fs_del must be the last thing we do, since it causes |
| 1235 | * sysfs to call function gfs2_sbd_release, which frees sdp. */ | ||
| 1236 | gfs2_sys_fs_del(sdp); | ||
| 1224 | sb->s_fs_info = NULL; | 1237 | sb->s_fs_info = NULL; |
| 1225 | return error; | 1238 | return error; |
| 1226 | } | 1239 | } |
| @@ -1390,10 +1403,9 @@ static void gfs2_kill_sb(struct super_block *sb) | |||
| 1390 | sdp->sd_root_dir = NULL; | 1403 | sdp->sd_root_dir = NULL; |
| 1391 | sdp->sd_master_dir = NULL; | 1404 | sdp->sd_master_dir = NULL; |
| 1392 | shrink_dcache_sb(sb); | 1405 | shrink_dcache_sb(sb); |
| 1393 | kill_block_super(sb); | ||
| 1394 | gfs2_delete_debugfs_file(sdp); | 1406 | gfs2_delete_debugfs_file(sdp); |
| 1395 | free_percpu(sdp->sd_lkstats); | 1407 | free_percpu(sdp->sd_lkstats); |
| 1396 | kfree(sdp); | 1408 | kill_block_super(sb); |
| 1397 | } | 1409 | } |
| 1398 | 1410 | ||
| 1399 | struct file_system_type gfs2_fs_type = { | 1411 | struct file_system_type gfs2_fs_type = { |
diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 9c2592b1d5ff..e4bee4bebbf6 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c | |||
| @@ -276,7 +276,15 @@ static struct attribute *gfs2_attrs[] = { | |||
| 276 | NULL, | 276 | NULL, |
| 277 | }; | 277 | }; |
| 278 | 278 | ||
| 279 | static void gfs2_sbd_release(struct kobject *kobj) | ||
| 280 | { | ||
| 281 | struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj); | ||
| 282 | |||
| 283 | kfree(sdp); | ||
| 284 | } | ||
| 285 | |||
| 279 | static struct kobj_type gfs2_ktype = { | 286 | static struct kobj_type gfs2_ktype = { |
| 287 | .release = gfs2_sbd_release, | ||
| 280 | .default_attrs = gfs2_attrs, | 288 | .default_attrs = gfs2_attrs, |
| 281 | .sysfs_ops = &gfs2_attr_ops, | 289 | .sysfs_ops = &gfs2_attr_ops, |
| 282 | }; | 290 | }; |
| @@ -583,6 +591,7 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp) | |||
| 583 | char ro[20]; | 591 | char ro[20]; |
| 584 | char spectator[20]; | 592 | char spectator[20]; |
| 585 | char *envp[] = { ro, spectator, NULL }; | 593 | char *envp[] = { ro, spectator, NULL }; |
| 594 | int sysfs_frees_sdp = 0; | ||
| 586 | 595 | ||
| 587 | sprintf(ro, "RDONLY=%d", (sb->s_flags & MS_RDONLY) ? 1 : 0); | 596 | sprintf(ro, "RDONLY=%d", (sb->s_flags & MS_RDONLY) ? 1 : 0); |
| 588 | sprintf(spectator, "SPECTATOR=%d", sdp->sd_args.ar_spectator ? 1 : 0); | 597 | sprintf(spectator, "SPECTATOR=%d", sdp->sd_args.ar_spectator ? 1 : 0); |
| @@ -591,8 +600,10 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp) | |||
| 591 | error = kobject_init_and_add(&sdp->sd_kobj, &gfs2_ktype, NULL, | 600 | error = kobject_init_and_add(&sdp->sd_kobj, &gfs2_ktype, NULL, |
| 592 | "%s", sdp->sd_table_name); | 601 | "%s", sdp->sd_table_name); |
| 593 | if (error) | 602 | if (error) |
| 594 | goto fail; | 603 | goto fail_reg; |
| 595 | 604 | ||
| 605 | sysfs_frees_sdp = 1; /* Freeing sdp is now done by sysfs calling | ||
| 606 | function gfs2_sbd_release. */ | ||
| 596 | error = sysfs_create_group(&sdp->sd_kobj, &tune_group); | 607 | error = sysfs_create_group(&sdp->sd_kobj, &tune_group); |
| 597 | if (error) | 608 | if (error) |
| 598 | goto fail_reg; | 609 | goto fail_reg; |
| @@ -615,9 +626,13 @@ fail_lock_module: | |||
| 615 | fail_tune: | 626 | fail_tune: |
| 616 | sysfs_remove_group(&sdp->sd_kobj, &tune_group); | 627 | sysfs_remove_group(&sdp->sd_kobj, &tune_group); |
| 617 | fail_reg: | 628 | fail_reg: |
| 618 | kobject_put(&sdp->sd_kobj); | 629 | free_percpu(sdp->sd_lkstats); |
| 619 | fail: | ||
| 620 | fs_err(sdp, "error %d adding sysfs files", error); | 630 | fs_err(sdp, "error %d adding sysfs files", error); |
| 631 | if (sysfs_frees_sdp) | ||
| 632 | kobject_put(&sdp->sd_kobj); | ||
| 633 | else | ||
| 634 | kfree(sdp); | ||
| 635 | sb->s_fs_info = NULL; | ||
| 621 | return error; | 636 | return error; |
| 622 | } | 637 | } |
| 623 | 638 | ||
