Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1074

kernel-2.6.18-238.el5.src.rpm

From: Dave Anderson <anderson@redhat.com>
Date: Wed, 25 Mar 2009 09:33:14 -0400
Subject: [fs] HFS: mount memory leak
Message-id: 49CA329A.6060800@redhat.com
O-Subject: [RHEL5.4 PATCH] BZ #488048: HFS mount memory leak
Bugzilla: 488048
RH-Acked-by: Larry Woodman <lwoodman@redhat.com>
RH-Acked-by: Eugene Teo <eugene@redhat.com>

BZ #488048: kernel: hfs: try_to_release_page() null pointer deref [rhel-5.4]
https://bugzilla.redhat.com/show_bug.cgi?id=488048

The implementation of the HFS mount procedure leaks a 2-page bitmap
upon the subsequent unmount, or as seen in this case, when the mount
command fails.

The BZ was based upon a panic seen running a QA test consisting of
repeated mount commands using a corrupted HFS filesystem image.
Each mount command would fail and the 2 pages leaked.

Eventually the system becomes starved for memory, and either:

(1) kswapd attempts to reclaim a inode page that the mount command
     is blocked waiting on for the I/O to complete.  It calls back into
     the hfs_releasepage(), which relies upon a pointer that has
     not been initialized by the blocked mount command, which is
     still waiting to get the lock on the page.  Or
(2) the oom-killer kicks in

The fix addresses the 2-page memory leak, and also prevents the
panic from occurring by making hfs_releasepage() recognize
the incomplete bookkeeping and fail the release-page request.

Tested by myself, Cai Quan and Eugene Teo.

diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index d05641c..187f031 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -65,6 +65,10 @@ static int hfs_releasepage(struct page *page, gfp_t mask)
 		BUG();
 		return 0;
 	}
+
+	if (!tree)
+		return 0;
+
 	if (tree->node_size >= PAGE_CACHE_SIZE) {
 		nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT);
 		spin_lock(&tree->hash_lock);
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index b4651e1..16b6a0a 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -349,6 +349,10 @@ void hfs_mdb_put(struct super_block *sb)
 	if (HFS_SB(sb)->nls_disk)
 		unload_nls(HFS_SB(sb)->nls_disk);
 
+	/* free the bitmap page(s) */
+	if (HFS_SB(sb)->bitmap)
+		free_pages((unsigned long)HFS_SB(sb)->bitmap, PAGE_SIZE < 8192 ? 1 : 0);
+
 	kfree(HFS_SB(sb));
 	sb->s_fs_info = NULL;
 }