summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2008-11-27 08:27:28 +0000
committerSteven Whitehouse <swhiteho@redhat.com>2009-01-05 07:39:14 +0000
commit3af165ac4d099385b12e3e75a9ee3ffd02da33e0 (patch)
treeb90552f6ac8db316c05d5f3246366cfa09cc7473 /fs
parent2e204703a1161e9bae38ba0d3d0df04a679e6f4f (diff)
downloadlinux-3af165ac4d099385b12e3e75a9ee3ffd02da33e0.tar.gz
linux-3af165ac4d099385b12e3e75a9ee3ffd02da33e0.tar.bz2
linux-3af165ac4d099385b12e3e75a9ee3ffd02da33e0.zip
GFS2: Fix use-after-free bug on umount
There was a use-after-free with the GFS2 super block during umount. This patch moves almost all of the umount code from ->put_super into ->kill_sb, the only bit that cannot be moved being the glock hash clearing which has to remain as ->put_super due to umount ordering requirements. As a result its now obvious that the kfree is the final operation, whereas before it was hidden in ->put_super. Also gfs2_jindex_free is then only referenced from a single file so thats moved and marked static too. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/gfs2/glock.c3
-rw-r--r--fs/gfs2/glock.h2
-rw-r--r--fs/gfs2/ops_fstype.c98
-rw-r--r--fs/gfs2/ops_super.c68
-rw-r--r--fs/gfs2/super.c34
-rw-r--r--fs/gfs2/super.h3
6 files changed, 94 insertions, 114 deletions
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 6e298b070117..5eae62e7f778 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -1547,8 +1547,9 @@ static void clear_glock(struct gfs2_glock *gl)
* Called when unmounting the filesystem.
*/
-void gfs2_gl_hash_clear(struct gfs2_sbd *sdp)
+void gfs2_gl_hash_clear(struct super_block *sb)
{
+ struct gfs2_sbd *sdp = sb->s_fs_info;
unsigned long t;
unsigned int x;
int cont;
diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h
index 543ec7ecfbda..ce54f338cff9 100644
--- a/fs/gfs2/glock.h
+++ b/fs/gfs2/glock.h
@@ -130,7 +130,7 @@ void gfs2_lvb_unhold(struct gfs2_glock *gl);
void gfs2_glock_cb(void *cb_data, unsigned int type, void *data);
void gfs2_reclaim_glock(struct gfs2_sbd *sdp);
-void gfs2_gl_hash_clear(struct gfs2_sbd *sdp);
+void gfs2_gl_hash_clear(struct super_block *sb);
void gfs2_glock_finish_truncate(struct gfs2_inode *ip);
int __init gfs2_glock_init(void);
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 4cae60f4a175..2e735bece6b9 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -705,6 +705,40 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
return error;
}
+/**
+ * gfs2_jindex_free - Clear all the journal index information
+ * @sdp: The GFS2 superblock
+ *
+ */
+
+static void gfs2_jindex_free(struct gfs2_sbd *sdp)
+{
+ struct list_head list, *head;
+ struct gfs2_jdesc *jd;
+ struct gfs2_journal_extent *jext;
+
+ spin_lock(&sdp->sd_jindex_spin);
+ list_add(&list, &sdp->sd_jindex_list);
+ list_del_init(&sdp->sd_jindex_list);
+ sdp->sd_journals = 0;
+ spin_unlock(&sdp->sd_jindex_spin);
+
+ while (!list_empty(&list)) {
+ jd = list_entry(list.next, struct gfs2_jdesc, jd_list);
+ head = &jd->extent_list;
+ while (!list_empty(head)) {
+ jext = list_entry(head->next,
+ struct gfs2_journal_extent,
+ extent_list);
+ list_del(&jext->extent_list);
+ kfree(jext);
+ }
+ list_del(&jd->jd_list);
+ iput(jd->jd_inode);
+ kfree(jd);
+ }
+}
+
static int init_journal(struct gfs2_sbd *sdp, int undo)
{
struct inode *master = sdp->sd_master_dir->d_inode;
@@ -1203,7 +1237,7 @@ fail_sb:
fail_locking:
init_locking(sdp, &mount_gh, UNDO);
fail_lm:
- gfs2_gl_hash_clear(sdp);
+ gfs2_gl_hash_clear(sb);
gfs2_lm_unmount(sdp);
while (invalidate_inodes(sb))
yield();
@@ -1263,17 +1297,61 @@ static int gfs2_get_sb_meta(struct file_system_type *fs_type, int flags,
static void gfs2_kill_sb(struct super_block *sb)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
- if (sdp) {
- gfs2_meta_syncfs(sdp);
- dput(sdp->sd_root_dir);
- dput(sdp->sd_master_dir);
- sdp->sd_root_dir = NULL;
- sdp->sd_master_dir = NULL;
+
+ if (sdp == NULL) {
+ kill_block_super(sb);
+ return;
}
- shrink_dcache_sb(sb);
+ gfs2_meta_syncfs(sdp);
+ dput(sdp->sd_root_dir);
+ dput(sdp->sd_master_dir);
+ sdp->sd_root_dir = NULL;
+ sdp->sd_master_dir = NULL;
+
+ /* Unfreeze the filesystem, if we need to */
+ mutex_lock(&sdp->sd_freeze_lock);
+ if (sdp->sd_freeze_count)
+ gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
+ mutex_unlock(&sdp->sd_freeze_lock);
+
+ kthread_stop(sdp->sd_quotad_process);
+ kthread_stop(sdp->sd_logd_process);
+ kthread_stop(sdp->sd_recoverd_process);
+
+ if (!(sb->s_flags & MS_RDONLY)) {
+ int error = gfs2_make_fs_ro(sdp);
+ if (error)
+ gfs2_io_error(sdp);
+ }
+
+ /* At this point, we're through modifying the disk */
+ gfs2_jindex_free(sdp);
+ gfs2_clear_rgrpd(sdp);
+ iput(sdp->sd_jindex);
+ iput(sdp->sd_inum_inode);
+ iput(sdp->sd_statfs_inode);
+ iput(sdp->sd_rindex);
+ iput(sdp->sd_quota_inode);
+
+ gfs2_glock_put(sdp->sd_rename_gl);
+ gfs2_glock_put(sdp->sd_trans_gl);
+
+ if (!sdp->sd_args.ar_spectator) {
+ gfs2_glock_dq_uninit(&sdp->sd_journal_gh);
+ gfs2_glock_dq_uninit(&sdp->sd_jinode_gh);
+ gfs2_glock_dq_uninit(&sdp->sd_ir_gh);
+ gfs2_glock_dq_uninit(&sdp->sd_sc_gh);
+ gfs2_glock_dq_uninit(&sdp->sd_qc_gh);
+ iput(sdp->sd_ir_inode);
+ iput(sdp->sd_sc_inode);
+ iput(sdp->sd_qc_inode);
+ }
+ gfs2_glock_dq_uninit(&sdp->sd_live_gh);
kill_block_super(sb);
- if (sdp)
- gfs2_delete_debugfs_file(sdp);
+ gfs2_lm_unmount(sdp);
+ gfs2_sys_fs_del(sdp);
+ gfs2_delete_debugfs_file(sdp);
+ kfree(sdp);
}
struct file_system_type gfs2_fs_type = {
diff --git a/fs/gfs2/ops_super.c b/fs/gfs2/ops_super.c
index 08837a728635..bd08a0a8d9bf 100644
--- a/fs/gfs2/ops_super.c
+++ b/fs/gfs2/ops_super.c
@@ -95,7 +95,7 @@ do_flush:
* Returns: errno
*/
-static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
+int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
{
struct gfs2_holder t_gh;
int error;
@@ -122,70 +122,6 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
}
/**
- * gfs2_put_super - Unmount the filesystem
- * @sb: The VFS superblock
- *
- */
-
-static void gfs2_put_super(struct super_block *sb)
-{
- struct gfs2_sbd *sdp = sb->s_fs_info;
- int error;
-
- /* Unfreeze the filesystem, if we need to */
-
- mutex_lock(&sdp->sd_freeze_lock);
- if (sdp->sd_freeze_count)
- gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
- mutex_unlock(&sdp->sd_freeze_lock);
-
- kthread_stop(sdp->sd_quotad_process);
- kthread_stop(sdp->sd_logd_process);
- kthread_stop(sdp->sd_recoverd_process);
-
- if (!(sb->s_flags & MS_RDONLY)) {
- error = gfs2_make_fs_ro(sdp);
- if (error)
- gfs2_io_error(sdp);
- }
- /* At this point, we're through modifying the disk */
-
- /* Release stuff */
-
- iput(sdp->sd_jindex);
- iput(sdp->sd_inum_inode);
- iput(sdp->sd_statfs_inode);
- iput(sdp->sd_rindex);
- iput(sdp->sd_quota_inode);
-
- gfs2_glock_put(sdp->sd_rename_gl);
- gfs2_glock_put(sdp->sd_trans_gl);
-
- if (!sdp->sd_args.ar_spectator) {
- gfs2_glock_dq_uninit(&sdp->sd_journal_gh);
- gfs2_glock_dq_uninit(&sdp->sd_jinode_gh);
- gfs2_glock_dq_uninit(&sdp->sd_ir_gh);
- gfs2_glock_dq_uninit(&sdp->sd_sc_gh);
- gfs2_glock_dq_uninit(&sdp->sd_qc_gh);
- iput(sdp->sd_ir_inode);
- iput(sdp->sd_sc_inode);
- iput(sdp->sd_qc_inode);
- }
-
- gfs2_glock_dq_uninit(&sdp->sd_live_gh);
- gfs2_clear_rgrpd(sdp);
- gfs2_jindex_free(sdp);
- /* Take apart glock structures and buffer lists */
- gfs2_gl_hash_clear(sdp);
- /* Unmount the locking protocol */
- gfs2_lm_unmount(sdp);
-
- /* At this point, we're through participating in the lockspace */
- gfs2_sys_fs_del(sdp);
- kfree(sdp);
-}
-
-/**
* gfs2_write_super
* @sb: the superblock
*
@@ -686,7 +622,7 @@ const struct super_operations gfs2_super_ops = {
.destroy_inode = gfs2_destroy_inode,
.write_inode = gfs2_write_inode,
.delete_inode = gfs2_delete_inode,
- .put_super = gfs2_put_super,
+ .put_super = gfs2_gl_hash_clear,
.write_super = gfs2_write_super,
.sync_fs = gfs2_sync_fs,
.write_super_lockfs = gfs2_write_super_lockfs,
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 141b781f2fcc..f14658b20204 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -33,40 +33,6 @@
#include "trans.h"
#include "util.h"
-/**
- * gfs2_jindex_free - Clear all the journal index information
- * @sdp: The GFS2 superblock
- *
- */
-
-void gfs2_jindex_free(struct gfs2_sbd *sdp)
-{
- struct list_head list, *head;
- struct gfs2_jdesc *jd;
- struct gfs2_journal_extent *jext;
-
- spin_lock(&sdp->sd_jindex_spin);
- list_add(&list, &sdp->sd_jindex_list);
- list_del_init(&sdp->sd_jindex_list);
- sdp->sd_journals = 0;
- spin_unlock(&sdp->sd_jindex_spin);
-
- while (!list_empty(&list)) {
- jd = list_entry(list.next, struct gfs2_jdesc, jd_list);
- head = &jd->extent_list;
- while (!list_empty(head)) {
- jext = list_entry(head->next,
- struct gfs2_journal_extent,
- extent_list);
- list_del(&jext->extent_list);
- kfree(jext);
- }
- list_del(&jd->jd_list);
- iput(jd->jd_inode);
- kfree(jd);
- }
-}
-
static struct gfs2_jdesc *jdesc_find_i(struct list_head *head, unsigned int jid)
{
struct gfs2_jdesc *jd;
diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h
index f6b8b00ad881..4d2492b3e7ef 100644
--- a/fs/gfs2/super.h
+++ b/fs/gfs2/super.h
@@ -25,8 +25,6 @@ static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp)
return x;
}
-void gfs2_jindex_free(struct gfs2_sbd *sdp);
-
struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid);
int gfs2_jdesc_check(struct gfs2_jdesc *jd);
@@ -34,6 +32,7 @@ int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename,
struct gfs2_inode **ipp);
int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
+int gfs2_make_fs_ro(struct gfs2_sbd *sdp);
int gfs2_statfs_init(struct gfs2_sbd *sdp);
void gfs2_statfs_change(struct gfs2_sbd *sdp,