From 05ebb55896d10a9737dad9ae0303f7f45489ba6f Mon Sep 17 00:00:00 2001 From: Grzegorz Antoniak Date: Sat, 13 Feb 2021 09:08:13 +0100 Subject: [PATCH] RAR5 reader: fixed out of bounds read in some files Added more range checks in the bit stream reading functions (read_bits_16 and read_bits_32) in order to better guard against out of memory reads. This commit contains a test with OSSFuzz sample #30448. Upstream-Status: Backport [https://git.launchpad.net/ubuntu/+source/libarchive/plain/debian/patches/CVE-2021-36976-1.patch?h=applied/3.4.3-2ubuntu0.1] CVE: CVE-2021-36976 Signed-off-by: Virendra Thakur --- Makefile.am | 1 + libarchive/archive_read_support_format_rar5.c | 108 ++++++++++-------- libarchive/test/test_read_format_rar5.c | 16 +++ ...r5_decode_number_out_of_bounds_read.rar.uu | 10 ++ 4 files changed, 89 insertions(+), 46 deletions(-) create mode 100644 libarchive/test/test_read_format_rar5_decode_number_out_of_bounds_read.rar.uu --- a/Makefile.am +++ b/Makefile.am @@ -883,6 +883,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar5_arm_filter_on_window_boundary.rar.uu \ libarchive/test/test_read_format_rar5_different_winsize_on_merge.rar.uu \ libarchive/test/test_read_format_rar5_block_size_is_too_small.rar.uu \ + libarchive/test/test_read_format_rar5_decode_number_out_of_bounds_read.rar.uu \ libarchive/test/test_read_format_raw.bufr.uu \ libarchive/test/test_read_format_raw.data.gz.uu \ libarchive/test/test_read_format_raw.data.Z.uu \ --- a/libarchive/archive_read_support_format_rar5.c +++ b/libarchive/archive_read_support_format_rar5.c @@ -1012,7 +1012,16 @@ static int read_var_sized(struct archive return ret; } -static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) { +static int read_bits_32(struct archive_read* a, struct rar5* rar, + const uint8_t* p, uint32_t* value) +{ + if(rar->bits.in_addr >= rar->cstate.cur_block_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Premature end of stream during extraction of data (#1)"); + return ARCHIVE_FATAL; + } + uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24; bits |= p[rar->bits.in_addr + 1] << 16; bits |= p[rar->bits.in_addr + 2] << 8; @@ -1023,7 +1032,16 @@ static int read_bits_32(struct rar5* rar return ARCHIVE_OK; } -static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) { +static int read_bits_16(struct archive_read* a, struct rar5* rar, + const uint8_t* p, uint16_t* value) +{ + if(rar->bits.in_addr >= rar->cstate.cur_block_size) { + archive_set_error(&a->archive, + ARCHIVE_ERRNO_PROGRAMMER, + "Premature end of stream during extraction of data (#2)"); + return ARCHIVE_FATAL; + } + int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16; bits |= (int) p[rar->bits.in_addr + 1] << 8; bits |= (int) p[rar->bits.in_addr + 2]; @@ -1039,8 +1057,8 @@ static void skip_bits(struct rar5* rar, } /* n = up to 16 */ -static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n, - int* value) +static int read_consume_bits(struct archive_read* a, struct rar5* rar, + const uint8_t* p, int n, int* value) { uint16_t v; int ret, num; @@ -1051,7 +1069,7 @@ static int read_consume_bits(struct rar5 return ARCHIVE_FATAL; } - ret = read_bits_16(rar, p, &v); + ret = read_bits_16(a, rar, p, &v); if(ret != ARCHIVE_OK) return ret; @@ -2425,13 +2443,13 @@ static int create_decode_tables(uint8_t* static int decode_number(struct archive_read* a, struct decode_table* table, const uint8_t* p, uint16_t* num) { - int i, bits, dist; + int i, bits, dist, ret; uint16_t bitfield; uint32_t pos; struct rar5* rar = get_context(a); - if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) { - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) { + return ret; } bitfield &= 0xfffe; @@ -2537,14 +2555,6 @@ static int parse_tables(struct archive_r for(i = 0; i < HUFF_TABLE_SIZE;) { uint16_t num; - if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) { - /* Truncated data, can't continue. */ - archive_set_error(&a->archive, - ARCHIVE_ERRNO_FILE_FORMAT, - "Truncated data in huffman tables (#2)"); - return ARCHIVE_FATAL; - } - ret = decode_number(a, &rar->cstate.bd, p, &num); if(ret != ARCHIVE_OK) { archive_set_error(&a->archive, @@ -2561,8 +2571,8 @@ static int parse_tables(struct archive_r /* 16..17: repeat previous code */ uint16_t n; - if(ARCHIVE_OK != read_bits_16(rar, p, &n)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n))) + return ret; if(num == 16) { n >>= 13; @@ -2590,8 +2600,8 @@ static int parse_tables(struct archive_r /* other codes: fill with zeroes `n` times */ uint16_t n; - if(ARCHIVE_OK != read_bits_16(rar, p, &n)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n))) + return ret; if(num == 18) { n >>= 13; @@ -2707,22 +2717,22 @@ static int parse_block_header(struct arc } /* Convenience function used during filter processing. */ -static int parse_filter_data(struct rar5* rar, const uint8_t* p, - uint32_t* filter_data) +static int parse_filter_data(struct archive_read* a, struct rar5* rar, + const uint8_t* p, uint32_t* filter_data) { - int i, bytes; + int i, bytes, ret; uint32_t data = 0; - if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes))) + return ret; bytes++; for(i = 0; i < bytes; i++) { uint16_t byte; - if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) { - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) { + return ret; } /* Cast to uint32_t will ensure the shift operation will not @@ -2765,16 +2775,17 @@ static int parse_filter(struct archive_r uint16_t filter_type; struct filter_info* filt = NULL; struct rar5* rar = get_context(ar); + int ret; /* Read the parameters from the input stream. */ - if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start))) + return ret; - if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length))) + return ret; - if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type))) + return ret; filter_type >>= 13; skip_bits(rar, 3); @@ -2814,8 +2825,8 @@ static int parse_filter(struct archive_r if(filter_type == FILTER_DELTA) { int channels; - if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels)) - return ARCHIVE_EOF; + if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels))) + return ret; filt->channels = channels + 1; } @@ -2823,10 +2834,11 @@ static int parse_filter(struct archive_r return ARCHIVE_OK; } -static int decode_code_length(struct rar5* rar, const uint8_t* p, - uint16_t code) +static int decode_code_length(struct archive_read* a, struct rar5* rar, + const uint8_t* p, uint16_t code) { int lbits, length = 2; + if(code < 8) { lbits = 0; length += code; @@ -2838,7 +2850,7 @@ static int decode_code_length(struct rar if(lbits > 0) { int add; - if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add)) + if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add)) return -1; length += add; @@ -2933,7 +2945,7 @@ static int do_uncompress_block(struct ar continue; } else if(num >= 262) { uint16_t dist_slot; - int len = decode_code_length(rar, p, num - 262), + int len = decode_code_length(a, rar, p, num - 262), dbits, dist = 1; @@ -2975,12 +2987,12 @@ static int do_uncompress_block(struct ar uint16_t low_dist; if(dbits > 4) { - if(ARCHIVE_OK != read_bits_32( - rar, p, &add)) { + if(ARCHIVE_OK != (ret = read_bits_32( + a, rar, p, &add))) { /* Return EOF if we * can't read more * data. */ - return ARCHIVE_EOF; + return ret; } skip_bits(rar, dbits - 4); @@ -3015,11 +3027,11 @@ static int do_uncompress_block(struct ar /* dbits is one of [0,1,2,3] */ int add; - if(ARCHIVE_OK != read_consume_bits(rar, - p, dbits, &add)) { + if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, + p, dbits, &add))) { /* Return EOF if we can't read * more data. */ - return ARCHIVE_EOF; + return ret; } dist += add; @@ -3076,7 +3088,11 @@ static int do_uncompress_block(struct ar return ARCHIVE_FATAL; } - len = decode_code_length(rar, p, len_slot); + len = decode_code_length(a, rar, p, len_slot); + if (len == -1) { + return ARCHIVE_FATAL; + } + rar->cstate.last_len = len; if(ARCHIVE_OK != copy_string(a, len, dist)) --- a/libarchive/test/test_read_format_rar5.c +++ b/libarchive/test/test_read_format_rar5.c @@ -1271,3 +1271,20 @@ DEFINE_TEST(test_read_format_rar5_block_ EPILOGUE(); } + +DEFINE_TEST(test_read_format_rar5_decode_number_out_of_bounds_read) +{ + /* oss fuzz 30448 */ + + char buf[4096]; + PROLOGUE("test_read_format_rar5_decode_number_out_of_bounds_read.rar"); + + /* Return codes of those calls are ignored, because this sample file + * is invalid. However, the unpacker shouldn't produce any SIGSEGV + * errors during processing. */ + + (void) archive_read_next_header(a, &ae); + while(0 < archive_read_data(a, buf, sizeof(buf))) {} + + EPILOGUE(); +} --- /dev/null +++ b/libarchive/test/test_read_format_rar5_decode_number_out_of_bounds_read.rar.uu @@ -0,0 +1,10 @@ +begin 644 test_read_format_rar5_decode_number_out_of_bounds_read.rar +M4F%R(1H'`0!3@"KT`P+G(@(0("`@@`L!!"`@("`@(($D_[BJ2"!::7!)210V +M+0#ZF#)Q!`+>YPW_("`@("``_R````````````````````````````!__P`` +M``````!T72`@/EW_(/\@("`@("`@("`@("`@("`@("`@("`@("`@(/\@("`@ +M("`@("#_("`@("`@("`@("`@("`@("`@("`@("`@("#_("`@("`@("`@_R`@ +M("`@("`@("`@("`@("`@("`@("`@("`@_R`@("`@("`@(/\@("`@("`@("`@ +M("`@("`@("`@("`@("`@(/\@("`@("`@("#_("`@("`@("`@("`@("`@("`@ +E("`@("`@("#_("`@("`@("`@_R`@("`@("`@("`@("`@("`@(``` +` +end