CVE-2023-29418: Out-of-bounds Read (CWE-125)
value: An issue was discovered in libbzip3.a in bzip3 before 1.2.3. There is an xwrite out-of-bounds read.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 6.5
Published: 2023-04-06T05:15Z
Repository: https://github.com/kspalaiologos/bzip3
Language: C
Files changed: src/libbz3.c, src/main.c
Commit date: 2023-03-19 14:25:04 +0100
Commit message: safety checks for decoding/testing when the buffer size and block size are inconsistent, tentative CRC32 version for BE systems (needs further verification on SPARC?)


Diff: diff --git a/src/libbz3.c b/src/libbz3.c
index 3a4bc0f..313dfe6 100644
--- a/src/libbz3.c
+++ b/src/libbz3.c
@@ -59,12 +59,19 @@ static const u32 crc32Table[256] = {
     0xE03E9C81L, 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
     0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
 };
 
 static u32 crc32sum(u32 crc, u8 * RESTRICT buf, size_t size) {
-    while (size--) crc = crc32Table[(crc ^ *(buf++)) & 0xff] ^ (crc >> 8);
-    return crc;
+    // Test endianness. The code needs to be different for LE and BE systems.
+    u32 test = 1;
+    if (*(u8 *) &test) {
+        while (size--) crc = crc32Table[(crc ^ *(buf++)) & 0xff] ^ (crc >> 8);
+        return crc;
+    } else {
+        while (size--) crc = crc32Table[((crc >> 24) ^ *(buf++)) & 0xff] ^ (crc << 8);
+        return crc;
+    }
 }
 
 /* LZP code. These constants were manually tuned to give the best compression ratio while using relatively
    little resources. The LZP dictionary is only around 1MiB in size and the minimum match length was chosen
    so that LZP would not interfere too much with the Burrows-Wheeler transform and the arithmetic coder, and
diff --git a/src/main.c b/src/main.c
index 712c510..f993b13 100644
--- a/src/main.c
+++ b/src/main.c
@@ -240,10 +240,14 @@ static int process(FILE * input_des, FILE * output_des, int mode, int block_size
                 if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) continue;
 
                 new_size = read_neutral_s32(byteswap_buf);
                 xread_noeof(&byteswap_buf, 1, 4, input_des);
                 old_size = read_neutral_s32(byteswap_buf);
+                if(old_size > block_size + 31) {
+                    fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
+                    return 1;
+                }
                 xread_noeof(buffer, 1, new_size, input_des);
                 bytes_read += 8 + new_size;
                 if (bz3_decode_block(state, buffer, new_size, old_size) == -1) {
                     fprintf(stderr, "Failed to decode a block: %s\n", bz3_strerror(state));
                     return 1;
@@ -257,10 +261,14 @@ static int process(FILE * input_des, FILE * output_des, int mode, int block_size
             while (!feof(input_des)) {
                 if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) continue;
                 new_size = read_neutral_s32(byteswap_buf);
                 xread_noeof(&byteswap_buf, 1, 4, input_des);
                 old_size = read_neutral_s32(byteswap_buf);
+                if(old_size > block_size + 31) {
+                    fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
+                    return 1;
+                }
                 xread_noeof(buffer, 1, new_size, input_des);
                 bytes_read += 8 + new_size;
                 if (bz3_decode_block(state, buffer, new_size, old_size) == -1) {
                     fprintf(stderr, "Failed to decode a block: %s\n", bz3_strerror(state));
                     return 1;
@@ -330,10 +338,14 @@ static int process(FILE * input_des, FILE * output_des, int mode, int block_size
                 for (; i < workers; i++) {
                     if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) break;
                     sizes[i] = read_neutral_s32(byteswap_buf);
                     xread_noeof(&byteswap_buf, 1, 4, input_des);
                     old_sizes[i] = read_neutral_s32(byteswap_buf);
+                    if(old_sizes[i] > block_size + 31) {
+                        fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
+                        return 1;
+                    }
                     xread_noeof(buffers[i], 1, sizes[i], input_des);
                     bytes_read += 8 + sizes[i];
                 }
                 bz3_decode_blocks(states, buffers, sizes, old_sizes, i);
                 for (s32 j = 0; j < i; j++) {
@@ -354,10 +366,14 @@ static int process(FILE * input_des, FILE * output_des, int mode, int block_size
                 for (; i < workers; i++) {
                     if (!xread_eofcheck(&byteswap_buf, 1, 4, input_des)) break;
                     sizes[i] = read_neutral_s32(byteswap_buf);
                     xread_noeof(&byteswap_buf, 1, 4, input_des);
                     old_sizes[i] = read_neutral_s32(byteswap_buf);
+                    if(old_sizes[i] > block_size + 31) {
+                        fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
+                        return 1;
+                    }
                     xread_noeof(buffers[i], 1, sizes[i], input_des);
                     bytes_read += 8 + sizes[i];
                 }
                 bz3_decode_blocks(states, buffers, sizes, old_sizes, i);
                 for (s32 j = 0; j < i; j++) {
@@ -689,10 +705,13 @@ int main(int argc, char * argv[]) {
     FILE *input_des = NULL, *output_des = NULL;
 
     output_des = mode != MODE_TEST ? open_output(output, force) : NULL;
     input_des = open_input(input);
 
+    if(output != f2)
+        free(output);
+
     int r = process(input_des, output_des, mode, block_size, workers, verbose, input);
 
     fclose(input_des);
     close_out_file(output_des);
     if (fclose(stdout)) {

Vulnerable code:     while (size--) crc = crc32Table[(crc ^ *(buf++)) & 0xff] ^ (crc >> 8);
    return crc;
Fixed code:     // Test endianness. The code needs to be different for LE and BE systems.
    u32 test = 1;
    if (*(u8 *) &test) {
        while (size--) crc = crc32Table[(crc ^ *(buf++)) & 0xff] ^ (crc >> 8);
        return crc;
    } else {
        while (size--) crc = crc32Table[((crc >> 24) ^ *(buf++)) & 0xff] ^ (crc << 8);
        return crc;
    }
                if(old_size > block_size + 31) {
                    fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
                    return 1;
                }
                if(old_size > block_size + 31) {
                    fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
                    return 1;
                }
                    if(old_sizes[i] > block_size + 31) {
                        fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
                        return 1;
                    }
                    if(old_sizes[i] > block_size + 31) {
                        fprintf(stderr, "Failed to decode a block: Inconsistent headers.\n");
                        return 1;
                    }
    if(output != f2)
        free(output);