mailing list archives
Re: CVE Request -- kernel: jbd/jbd2: invalid value of first log block leads to oops
From: Solar Designer <solar () openwall com>
Date: Mon, 6 Feb 2012 09:25:57 +0400
On Sun, Nov 13, 2011 at 08:55:00AM -0700, Kurt Seifried wrote:
On 11/11/2011 03:50 PM, Petr Matousek wrote:
A flaw was found in the way Linux kernel's Journaling Block Device (JBD)
handled invalid log first block value. An attacker able to mount
malicious ext3 or ext4 image could use this flaw to crash the system.
CVE-2011-4132 is for kernel: jbd/jbd2: invalid value of first log block
leads to oops
http://rhn.redhat.com/errata/RHSA-2012-0007.html says "A flaw was found
in the Linux kernel's Journaling Block Device (JBD).
A local attacker could use this flaw to crash the system by mounting a
specially-crafted ext3 or ext4 disk. (CVE-2011-4132, Moderate)"
Even though the issue is of little relevance for us due to its attack
vector, I wanted to see if it's in fact limited to a DoS or maybe not.
Red Hat Bugzilla:
The commit message is lengthy and it includes a reproducer script (I am
pleasantly surprised). It says that for ext3 an assert failure occurs
when s_first == 0, so this case does in fact feel like just a DoS at
first glance at the commit message (not sure whether the same applies to
ext4, though). (But please read below.) However, the fix also adds a
check to reject "be32_to_cpu(sb->s_first) >= journal->j_maxlen", and it
is non-obvious what happens when s_first is above this limit.
Trying to follow the code, it appears that without the fix the
journal_get_superblock() call from load_superblock() does not return an
error for these invalid values of s_first, so we get to:
journal->j_first = be32_to_cpu(sb->s_first);
j_first in turn gets into other fields in journal_reset() or/and in
journal_next_log_block(). However, journal_reset() has:
journal->j_free = last - first;
and journal_next_log_block() has:
J_ASSERT(journal->j_free > 1);
This could save us from worse-than-DoS impact, but j_free is unsigned
(so would-be-nagative values would look valid to the assert) and
additionally J_ASSERT() is jbd.h (unlike jbd2.h's) looks like it will
happily return control after merely dumping a backtrace.
...Speaking of the latter, this means that the assert mentioned in the
commit message is probably also insufficient to guarantee that there's
no worse-than-DoS impact for the "== 0" case.
journal_next_log_block() may set:
journal->j_head = journal->j_first;
and the next call to it may do:
blocknr = journal->j_head;
return journal_bmap(journal, blocknr, retp);
ret = bmap(journal->j_inode, blocknr);
Thus, it appears that we may get an almost arbitrary out of range block
number passed down into:
sector_t bmap(struct inode * inode, sector_t block)
sector_t res = 0;
res = inode->i_mapping->a_ops->bmap(inode->i_mapping, block);
Following my guess as to what the ->bmap pointer might be here (note: I
am not familiar with this code at all), I looked at ext3_bmap() and
ext4_bmap(). These simply pass the block number into
generic_block_bmap(), and they also pass a pointer to ext3_get_block()
and ext4_get_block(), respectively. generic_block_bmap() does little
besides calling the specific get_block() function via the pointer, but
those fs-specific functions and those they call are non-trivial.
That's where I stopped spending/wasting my time on this, concluding that
I still do not know if it's just a DoS (likely) or worse (possibly).
If this were of more relevance to us, I'd probably figure it out, but as
it is I just leave it for someone more curious or actually requiring
proof. Sorry for this xorl'ish "analysis"; I was hoping I'd arrive at
something more useful, but I just ran out of time on this. ;-(
The only maybe-useful outcome is another confirmation that this kind of
issues are rarely quick and easy to fully analyze and figure out their
- Re: CVE Request -- kernel: jbd/jbd2: invalid value of first log block leads to oops Solar Designer (Feb 06)