From patchwork Tue Feb 2 22:43:09 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Morton X-Patchwork-Id: 44320 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id C51BEB7D53 for ; Wed, 3 Feb 2010 09:45:32 +1100 (EST) Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1NcRT7-0003Ze-DV; Tue, 02 Feb 2010 22:43:37 +0000 Received: from smtp1.linux-foundation.org ([140.211.169.13]) by bombadil.infradead.org with esmtps (Exim 4.69 #1 (Red Hat Linux)) id 1NcRT0-0003X3-Mk; Tue, 02 Feb 2010 22:43:35 +0000 Received: from imap1.linux-foundation.org (imap1.linux-foundation.org [140.211.169.55]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id o12Mh9h6011828 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 2 Feb 2010 14:43:10 -0800 Received: from localhost.localdomain (localhost [127.0.0.1]) by imap1.linux-foundation.org (8.13.5.20060308/8.13.5/Debian-3ubuntu1.1) with ESMTP id o12Mh9er019176; Tue, 2 Feb 2010 14:43:09 -0800 Message-Id: <201002022243.o12Mh9er019176@imap1.linux-foundation.org> Subject: [patch 1/5] jffs2: fix memory corruption in jffs2_read_inode_range() To: dwmw2@infradead.org From: akpm@linux-foundation.org Date: Tue, 02 Feb 2010 14:43:09 -0800 MIME-Version: 1.0 X-Spam-Status: No, hits=-3.518 required=5 tests=AWL, BAYES_00, OSDL_HEADER_SUBJECT_BRACKETED X-Spam-Checker-Version: SpamAssassin 3.2.4-osdl_revision__1.47__ X-MIMEDefang-Filter: lf$Revision: 1.188 $ X-Scanned-By: MIMEDefang 2.63 on 140.211.169.13 X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20100202_174331_061969_43CAC55D X-CRM114-Status: GOOD ( 19.47 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.2.5 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- _SUMMARY_ Cc: neilb@suse.de, akpm@linux-foundation.org, avorontsov@ru.mvista.com, linux-mtd@lists.infradead.org X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org From: Anton Vorontsov In 2.6.23 kernel, commit a32ea1e1f925399e0d81ca3f7394a44a6dafa12c ("Fix read/truncate race") fixed a race in the generic code, and as a side effect, now do_generic_file_read() can ask us to readpage() past the i_size, which seems to be correctly handled by the block routines (e.g. block_read_full_page() fills the page with zeroes in case if somebody is trying to read past the last inode's block). JFFS2 doesn't handle this, instead jffs2_read_inode_range() is trying to lookup the fragments which do not belong to anything, and jffs2_lookup_node_frag() happily returns "the closest smaller match" which is OK for most cases (I guess), but in our case there wasn't anything meaningful to lookup in the first place. After we found the bogus fragment, the code can branch to 'Filling frag hole' case that does memset(buf, 0, min(end, frag->ofs + frag->size) - offset); and since 'frag' is bogus, its ofs + size can be smaller than the real 'offset' (i.e. index << PAGE_CACHE_SHIFT), and so the code turns into memset(buf, 0, ); Hopefully, in most cases the corruption is fatal, and quickly causing random oopses, like this: root@10.0.0.4:~/ltp-fs-20090531# ./testcases/kernel/fs/ftest/ftest01 Unable to handle kernel paging request for data at address 0x00000008 Faulting instruction address: 0xc01cd980 Oops: Kernel access of bad area, sig: 11 [#1] [...] NIP [c01cd980] rb_insert_color+0x38/0x184 LR [c0043978] enqueue_hrtimer+0x88/0xc4 Call Trace: [c6c63b60] [c004f9a8] tick_sched_timer+0xa0/0xe4 (unreliable) [c6c63b80] [c0043978] enqueue_hrtimer+0x88/0xc4 [c6c63b90] [c0043a48] __run_hrtimer+0x94/0xbc [c6c63bb0] [c0044628] hrtimer_interrupt+0x140/0x2b8 [c6c63c10] [c000f8e8] timer_interrupt+0x13c/0x254 [c6c63c30] [c001352c] ret_from_except+0x0/0x14 --- Exception: 901 at memset+0x38/0x5c LR = jffs2_read_inode_range+0x144/0x17c [c6c63cf0] [00000000] (null) (unreliable) This patch fixes the issue, plus fixes all LTP tests on NAND/UBI with JFFS2 filesystem that were failing since 2.6.23 (seems like the bug above also broke the truncation). Note that the bug is only reproducible with write-buffered MTD devices (e.g. NAND, UBI), which is a mystery to me (though I didn't look close enough, possibly there is a race between gc and wbuf code, but I don't see it and obviously it isn't that fatal). Signed-off-by: Anton Vorontsov Cc: David Woodhouse Cc: Neil Brown Signed-off-by: Andrew Morton --- fs/jffs2/file.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff -puN fs/jffs2/file.c~jffs2-fix-memory-corruption-in-jffs2_read_inode_range fs/jffs2/file.c --- a/fs/jffs2/file.c~jffs2-fix-memory-corruption-in-jffs2_read_inode_range +++ a/fs/jffs2/file.c @@ -85,7 +85,13 @@ static int jffs2_do_readpage_nolock (str pg_buf = kmap(pg); /* FIXME: Can kmap fail? */ - ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); + if (pg->index > ((i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT)) { + ret = 0; + memset(pg_buf, 0, PAGE_CACHE_SIZE); + } else { + ret = jffs2_read_inode_range(c, f, pg_buf, + pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); + } if (ret) { ClearPageUptodate(pg);