Skip to content

Commit

Permalink
jbd2: fix r_count overflows leading to buffer overflow in journal rec…
Browse files Browse the repository at this point in the history
…overy

The journal revoke block recovery code does not check r_count for
sanity, which means that an evil value of r_count could result in
the kernel reading off the end of the revoke table and into whatever
garbage lies beyond.  This could crash the kernel, so fix that.

However, in testing this fix, I discovered that the code to write
out the revoke tables also was not correctly checking to see if the
block was full -- the current offset check is fine so long as the
revoke table space size is a multiple of the record size, but this
is not true when either journal_csum_v[23] are set.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Jan Kara <jack@suse.cz>
Cc: stable@vger.kernel.org
  • Loading branch information
Darrick J. Wong authored and Theodore Ts'o committed May 14, 2015
1 parent 2f97486 commit e531d0b
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 9 deletions.
10 changes: 9 additions & 1 deletion fs/jbd2/recovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -842,15 +842,23 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
{
jbd2_journal_revoke_header_t *header;
int offset, max;
int csum_size = 0;
__u32 rcount;
int record_len = 4;

header = (jbd2_journal_revoke_header_t *) bh->b_data;
offset = sizeof(jbd2_journal_revoke_header_t);
max = be32_to_cpu(header->r_count);
rcount = be32_to_cpu(header->r_count);

if (!jbd2_revoke_block_csum_verify(journal, header))
return -EINVAL;

if (jbd2_journal_has_csum_v2or3(journal))
csum_size = sizeof(struct jbd2_journal_revoke_tail);
if (rcount > journal->j_blocksize - csum_size)
return -EINVAL;
max = rcount;

if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
record_len = 8;

Expand Down
18 changes: 10 additions & 8 deletions fs/jbd2/revoke.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ static void write_one_revoke_record(journal_t *journal,
{
int csum_size = 0;
struct buffer_head *descriptor;
int offset;
int sz, offset;
journal_header_t *header;

/* If we are already aborting, this all becomes a noop. We
Expand All @@ -594,9 +594,14 @@ static void write_one_revoke_record(journal_t *journal,
if (jbd2_journal_has_csum_v2or3(journal))
csum_size = sizeof(struct jbd2_journal_revoke_tail);

if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
sz = 8;
else
sz = 4;

/* Make sure we have a descriptor with space left for the record */
if (descriptor) {
if (offset >= journal->j_blocksize - csum_size) {
if (offset + sz > journal->j_blocksize - csum_size) {
flush_descriptor(journal, descriptor, offset, write_op);
descriptor = NULL;
}
Expand All @@ -619,16 +624,13 @@ static void write_one_revoke_record(journal_t *journal,
*descriptorp = descriptor;
}

if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) {
if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
* ((__be64 *)(&descriptor->b_data[offset])) =
cpu_to_be64(record->blocknr);
offset += 8;

} else {
else
* ((__be32 *)(&descriptor->b_data[offset])) =
cpu_to_be32(record->blocknr);
offset += 4;
}
offset += sz;

*offsetp = offset;
}
Expand Down

0 comments on commit e531d0b

Please sign in to comment.