]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
GFS2: Add kobject release method
authorBob Peterson <rpeterso@redhat.com>
Wed, 13 Jun 2012 14:27:41 +0000 (10:27 -0400)
committerSteven Whitehouse <swhiteho@redhat.com>
Wed, 13 Jun 2012 14:59:48 +0000 (15:59 +0100)
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>
fs/gfs2/ops_fstype.c
fs/gfs2/sys.c

index b8c250fc4922e8dd501a0c12c1481f7d1a32e0d8..9b2389756acd6ab4e8c23d51192144bb9f18761c 100644 (file)
@@ -1118,20 +1118,33 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
        }
 
        error = init_names(sdp, silent);
-       if (error)
-               goto fail;
+       if (error) {
+               /* 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);
 
-       gfs2_create_debugfs_file(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)
-               goto fail;
+               return error;
+
+       gfs2_create_debugfs_file(sdp);
 
        error = gfs2_lm_mount(sdp, silent);
        if (error)
-               goto fail_sys;
+               goto fail_debug;
 
        error = init_locking(sdp, &mount_gh, DO);
        if (error)
@@ -1215,12 +1228,12 @@ fail_locking:
 fail_lm:
        gfs2_gl_hash_clear(sdp);
        gfs2_lm_unmount(sdp);
-fail_sys:
-       gfs2_sys_fs_del(sdp);
-fail:
+fail_debug:
        gfs2_delete_debugfs_file(sdp);
        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;
        return error;
 }
@@ -1390,10 +1403,9 @@ static void gfs2_kill_sb(struct super_block *sb)
        sdp->sd_root_dir = NULL;
        sdp->sd_master_dir = NULL;
        shrink_dcache_sb(sb);
-       kill_block_super(sb);
        gfs2_delete_debugfs_file(sdp);
        free_percpu(sdp->sd_lkstats);
-       kfree(sdp);
+       kill_block_super(sb);
 }
 
 struct file_system_type gfs2_fs_type = {
index 9c2592b1d5ff74ab0e5c2f800b0d0e4508119021..e4bee4bebbf69153e9333633bc9754bf71a6fcaf 100644 (file)
@@ -276,7 +276,15 @@ static struct attribute *gfs2_attrs[] = {
        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 = {
+       .release = gfs2_sbd_release,
        .default_attrs = gfs2_attrs,
        .sysfs_ops     = &gfs2_attr_ops,
 };
@@ -583,6 +591,7 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
        char ro[20];
        char spectator[20];
        char *envp[] = { ro, spectator, NULL };
+       int sysfs_frees_sdp = 0;
 
        sprintf(ro, "RDONLY=%d", (sb->s_flags & MS_RDONLY) ? 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,
                                     "%s", sdp->sd_table_name);
        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);
        if (error)
                goto fail_reg;
@@ -615,9 +626,13 @@ fail_lock_module:
 fail_tune:
        sysfs_remove_group(&sdp->sd_kobj, &tune_group);
 fail_reg:
-       kobject_put(&sdp->sd_kobj);
-fail:
+       free_percpu(sdp->sd_lkstats);
        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;
 }