aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jbd2
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2015-05-14 19:11:50 -0400
committerTheodore Ts'o <tytso@mit.edu>2015-05-14 19:11:50 -0400
commite531d0bceb402e643a4499de40dd3fa39d8d2e43 (patch)
treec5964d8337898f3fcb04a78adb7a5297d2ac8151 /fs/jbd2
parent2f974865ffdfe7b9f46a9940836c8b167342563d (diff)
jbd2: fix r_count overflows leading to buffer overflow in journal recovery
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
Diffstat (limited to 'fs/jbd2')
-rw-r--r--fs/jbd2/recovery.c10
-rw-r--r--fs/jbd2/revoke.c18
2 files changed, 19 insertions, 9 deletions
diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c
index b5128c6e63ad..a9079d035ae5 100644
--- a/fs/jbd2/recovery.c
+++ b/fs/jbd2/recovery.c
@@ -842,15 +842,23 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
842{ 842{
843 jbd2_journal_revoke_header_t *header; 843 jbd2_journal_revoke_header_t *header;
844 int offset, max; 844 int offset, max;
845 int csum_size = 0;
846 __u32 rcount;
845 int record_len = 4; 847 int record_len = 4;
846 848
847 header = (jbd2_journal_revoke_header_t *) bh->b_data; 849 header = (jbd2_journal_revoke_header_t *) bh->b_data;
848 offset = sizeof(jbd2_journal_revoke_header_t); 850 offset = sizeof(jbd2_journal_revoke_header_t);
849 max = be32_to_cpu(header->r_count); 851 rcount = be32_to_cpu(header->r_count);
850 852
851 if (!jbd2_revoke_block_csum_verify(journal, header)) 853 if (!jbd2_revoke_block_csum_verify(journal, header))
852 return -EINVAL; 854 return -EINVAL;
853 855
856 if (jbd2_journal_has_csum_v2or3(journal))
857 csum_size = sizeof(struct jbd2_journal_revoke_tail);
858 if (rcount > journal->j_blocksize - csum_size)
859 return -EINVAL;
860 max = rcount;
861
854 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) 862 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
855 record_len = 8; 863 record_len = 8;
856 864
diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c
index c6cbaef2bda1..14214da80eb8 100644
--- a/fs/jbd2/revoke.c
+++ b/fs/jbd2/revoke.c
@@ -577,7 +577,7 @@ static void write_one_revoke_record(journal_t *journal,
577{ 577{
578 int csum_size = 0; 578 int csum_size = 0;
579 struct buffer_head *descriptor; 579 struct buffer_head *descriptor;
580 int offset; 580 int sz, offset;
581 journal_header_t *header; 581 journal_header_t *header;
582 582
583 /* If we are already aborting, this all becomes a noop. We 583 /* If we are already aborting, this all becomes a noop. We
@@ -594,9 +594,14 @@ static void write_one_revoke_record(journal_t *journal,
594 if (jbd2_journal_has_csum_v2or3(journal)) 594 if (jbd2_journal_has_csum_v2or3(journal))
595 csum_size = sizeof(struct jbd2_journal_revoke_tail); 595 csum_size = sizeof(struct jbd2_journal_revoke_tail);
596 596
597 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
598 sz = 8;
599 else
600 sz = 4;
601
597 /* Make sure we have a descriptor with space left for the record */ 602 /* Make sure we have a descriptor with space left for the record */
598 if (descriptor) { 603 if (descriptor) {
599 if (offset >= journal->j_blocksize - csum_size) { 604 if (offset + sz > journal->j_blocksize - csum_size) {
600 flush_descriptor(journal, descriptor, offset, write_op); 605 flush_descriptor(journal, descriptor, offset, write_op);
601 descriptor = NULL; 606 descriptor = NULL;
602 } 607 }
@@ -619,16 +624,13 @@ static void write_one_revoke_record(journal_t *journal,
619 *descriptorp = descriptor; 624 *descriptorp = descriptor;
620 } 625 }
621 626
622 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) { 627 if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT))
623 * ((__be64 *)(&descriptor->b_data[offset])) = 628 * ((__be64 *)(&descriptor->b_data[offset])) =
624 cpu_to_be64(record->blocknr); 629 cpu_to_be64(record->blocknr);
625 offset += 8; 630 else
626
627 } else {
628 * ((__be32 *)(&descriptor->b_data[offset])) = 631 * ((__be32 *)(&descriptor->b_data[offset])) =
629 cpu_to_be32(record->blocknr); 632 cpu_to_be32(record->blocknr);
630 offset += 4; 633 offset += sz;
631 }
632 634
633 *offsetp = offset; 635 *offsetp = offset;
634} 636}