]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: fix buffer overrun in revoke block scanning
authorDarrick J. Wong <darrick.wong@oracle.com>
Sun, 17 May 2015 00:50:21 +0000 (20:50 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 17 May 2015 00:50:21 +0000 (20:50 -0400)
Check the value of r_count to ensure that we never try to read revoke
records past the end of the revoke block.  It turns out that the
journal writing code in debugfs was also playing fast and loose with
the r_count, so fix that as well.

The Coverity bug was 1297508.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
debugfs/do_journal.c
e2fsck/recovery.c
e2fsck/revoke.c
tests/j_corrupt_revoke_rcount/expect.1 [new file with mode: 0644]
tests/j_corrupt_revoke_rcount/expect.2 [new file with mode: 0644]
tests/j_corrupt_revoke_rcount/image.gz [new file with mode: 0644]
tests/j_corrupt_revoke_rcount/name [new file with mode: 0644]

index 46d1793460e8be79a18965eade3ad26f7c736485..22322e5bcca9045e9e83a6e0872ebea696baa27b 100644 (file)
@@ -175,7 +175,7 @@ static errcode_t journal_add_revoke_to_trans(journal_transaction_t *trans,
        void *buf;
        size_t i, offset;
        blk64_t curr_blk;
-       int csum_size = 0;
+       int sz, csum_size = 0;
        struct buffer_head *bh;
        errcode_t err;
 
@@ -204,9 +204,15 @@ static errcode_t journal_add_revoke_to_trans(journal_transaction_t *trans,
        jrb->r_header.h_sequence = ext2fs_cpu_to_be32(trans->tid);
        offset = sizeof(*jrb);
 
+       if (JFS_HAS_INCOMPAT_FEATURE(trans->journal,
+                                    JFS_FEATURE_INCOMPAT_64BIT))
+               sz = 8;
+       else
+               sz = 4;
+
        for (i = 0; i < revoke_len; i++) {
                /* Block full, write to journal */
-               if (offset > trans->journal->j_blocksize - csum_size) {
+               if (offset + sz > trans->journal->j_blocksize - csum_size) {
                        jrb->r_count = ext2fs_cpu_to_be32(offset);
                        jbd2_revoke_csum_set(trans->journal, bh);
 
@@ -233,16 +239,13 @@ static errcode_t journal_add_revoke_to_trans(journal_transaction_t *trans,
                }
 
                if (JFS_HAS_INCOMPAT_FEATURE(trans->journal,
-                                            JFS_FEATURE_INCOMPAT_64BIT)) {
+                                            JFS_FEATURE_INCOMPAT_64BIT))
                        *((__u64 *)(&((char *)buf)[offset])) =
                                ext2fs_cpu_to_be64(revoke_list[i]);
-                       offset += 8;
-
-               } else {
+               else
                        *((__u32 *)(&((char *)buf)[offset])) =
                                ext2fs_cpu_to_be32(revoke_list[i]);
-                       offset += 4;
-               }
+               offset += sz;
        }
 
        if (offset > 0) {
index b5ce3b3c48da33ede55b10397c01fa855dda3260..d5244be224eae24b964639904a63a63d5f198899 100644 (file)
@@ -839,15 +839,23 @@ static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
 {
        journal_revoke_header_t *header;
        int offset, max;
+       int csum_size = 0;
+       __u32 rcount;
        int record_len = 4;
 
        header = (journal_revoke_header_t *) bh->b_data;
        offset = sizeof(journal_revoke_header_t);
-       max = ext2fs_be32_to_cpu(header->r_count);
+       rcount = ext2fs_be32_to_cpu(header->r_count);
 
        if (!jbd2_revoke_block_csum_verify(journal, header))
                return -EINVAL;
 
+       if (journal_has_csum_v2or3(journal))
+               csum_size = sizeof(struct journal_revoke_tail);
+       if (rcount > journal->j_blocksize - csum_size)
+               return -EINVAL;
+       max = rcount;
+
        if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT))
                record_len = 8;
 
index b4c3f5f46a85daf14ee7004f81f38a5a6c558e1a..0543099725af45c898c3c53a6356ac7edc152778 100644 (file)
@@ -583,7 +583,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
@@ -600,9 +600,14 @@ static void write_one_revoke_record(journal_t *journal,
        if (journal_has_csum_v2or3(journal))
                csum_size = sizeof(struct 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;
                }
@@ -625,16 +630,13 @@ static void write_one_revoke_record(journal_t *journal,
                *descriptorp = descriptor;
        }
 
-       if (JFS_HAS_INCOMPAT_FEATURE(journal, JFS_FEATURE_INCOMPAT_64BIT)) {
-               * ((__u64 *)(&descriptor->b_data[offset])) =
-                       ext2fs_cpu_to_be64(record->blocknr);
-               offset += 8;
-
-       } else {
-               * ((__u32 *)(&descriptor->b_data[offset])) =
-                       ext2fs_cpu_to_be32(record->blocknr);
-               offset += 4;
-       }
+       if (JBD2_HAS_INCOMPAT_FEATURE(journal, JBD2_FEATURE_INCOMPAT_64BIT)) {
+               * ((__be64 *)(&descriptor->b_data[offset])) =
+                       cpu_to_be64(record->blocknr);
+       else
+               * ((__be32 *)(&descriptor->b_data[offset])) =
+                       cpu_to_be32(record->blocknr);
+       offset += sz;
 
        *offsetp = offset;
 }
diff --git a/tests/j_corrupt_revoke_rcount/expect.1 b/tests/j_corrupt_revoke_rcount/expect.1
new file mode 100644 (file)
index 0000000..97324f3
--- /dev/null
@@ -0,0 +1,8 @@
+test_filesys: recovering journal
+../e2fsck/e2fsck: Invalid argument while recovering ext3 journal of test_filesys
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+
+test_filesys: ********** WARNING: Filesystem still has errors **********
+
+Exit status is 12
diff --git a/tests/j_corrupt_revoke_rcount/expect.2 b/tests/j_corrupt_revoke_rcount/expect.2
new file mode 100644 (file)
index 0000000..c569901
--- /dev/null
@@ -0,0 +1,8 @@
+test_filesys: recovering journal
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 11/512 files (9.1% non-contiguous), 1066/2048 blocks
+Exit status is 0
diff --git a/tests/j_corrupt_revoke_rcount/image.gz b/tests/j_corrupt_revoke_rcount/image.gz
new file mode 100644 (file)
index 0000000..c8b19e8
Binary files /dev/null and b/tests/j_corrupt_revoke_rcount/image.gz differ
diff --git a/tests/j_corrupt_revoke_rcount/name b/tests/j_corrupt_revoke_rcount/name
new file mode 100644 (file)
index 0000000..92b523e
--- /dev/null
@@ -0,0 +1 @@
+corrupt revoke r_count buffer overflow