From: Martin Matuška Date: Thu, 14 Aug 2025 21:20:55 +0000 (+0200) Subject: Merge pull request #2716 from antekone/bug/GH-2714/infinite-loop/1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ab5ae538f6eef4962f43a7561cba14f4f40f63c0;p=thirdparty%2Flibarchive.git Merge pull request #2716 from antekone/bug/GH-2714/infinite-loop/1 RAR5 reader: early fail when file declares data for a dir entry (cherry picked from commit 2db13f74226f766ef776dbe74654bcd7120a19e8) --- diff --git a/Makefile.am b/Makefile.am index 9d8873d1d..05ff1b7c4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -955,6 +955,7 @@ libarchive_test_EXTRA_DIST=\ libarchive/test/test_read_format_rar5_multiple_files.rar.uu \ libarchive/test/test_read_format_rar5_multiple_files_solid.rar.uu \ libarchive/test/test_read_format_rar5_nonempty_dir_stream.rar.uu \ + libarchive/test/test_read_format_rar5_dirdata.rar.uu \ libarchive/test/test_read_format_rar5_owner.rar.uu \ libarchive/test/test_read_format_rar5_readtables_overflow.rar.uu \ libarchive/test/test_read_format_rar5_sfx.exe.uu \ diff --git a/libarchive/archive_read_support_format_rar5.c b/libarchive/archive_read_support_format_rar5.c index f2af82cfa..17e501e02 100644 --- a/libarchive/archive_read_support_format_rar5.c +++ b/libarchive/archive_read_support_format_rar5.c @@ -1703,6 +1703,33 @@ static int process_head_file_extra(struct archive_read* a, return ARCHIVE_OK; } +static int file_entry_sanity_checks(struct archive_read* a, + size_t block_flags, uint8_t is_dir, uint64_t unpacked_size, + size_t packed_size) +{ + if (is_dir) { + const int declares_data_size = + (int) (unpacked_size != 0 || packed_size != 0); + + /* FILE entries for directories still declare HFL_DATA in block flags, + even though attaching data to such blocks doesn't make much sense. */ + if (declares_data_size) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "directory entries cannot have any data"); + return ARCHIVE_FATAL; + } + } else { + const int declares_hfl_data = (int) ((block_flags & HFL_DATA) != 0); + if (!declares_hfl_data) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, + "no data found in file/service block"); + return ARCHIVE_FATAL; + } + } + + return ARCHIVE_OK; +} + static int process_head_file(struct archive_read* a, struct rar5* rar, struct archive_entry* entry, size_t block_flags) { @@ -1718,6 +1745,7 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, int c_method = 0, c_version = 0; char name_utf8_buf[MAX_NAME_IN_BYTES]; const uint8_t* p; + int sanity_ret; enum FILE_FLAGS { DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004, @@ -1761,10 +1789,6 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, rar->file.bytes_remaining = data_size; } else { rar->file.bytes_remaining = 0; - - archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, - "no data found in file/service block"); - return ARCHIVE_FATAL; } if(!read_var_sized(a, &file_flags, NULL)) @@ -1781,6 +1805,13 @@ static int process_head_file(struct archive_read* a, struct rar5* rar, rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0); + sanity_ret = file_entry_sanity_checks(a, block_flags, rar->file.dir, + unpacked_size, data_size); + + if (sanity_ret != ARCHIVE_OK) { + return sanity_ret; + } + if(!read_var_sized(a, &file_attr, NULL)) return ARCHIVE_EOF; @@ -4180,7 +4211,7 @@ static int rar5_read_data(struct archive_read *a, const void **buff, * it's impossible to perform any decompression. */ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't decompress an entry marked as a directory"); - return ARCHIVE_FAILED; + return ARCHIVE_FATAL; } if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) { diff --git a/libarchive/test/test_read_format_rar5.c b/libarchive/test/test_read_format_rar5.c index 202b66f68..6ab0d236a 100644 --- a/libarchive/test/test_read_format_rar5.c +++ b/libarchive/test/test_read_format_rar5.c @@ -1111,6 +1111,18 @@ DEFINE_TEST(test_read_format_rar5_nonempty_dir_stream) EPILOGUE(); } +DEFINE_TEST(test_read_format_rar5_nonempty_dir_data) +{ + PROLOGUE("test_read_format_rar5_dirdata.rar"); + + /* This archive is invalid. It declares a directory entry with nonzero + data size. */ + + assertA(archive_read_next_header(a, &ae) == ARCHIVE_FATAL); + + EPILOGUE(); +} + DEFINE_TEST(test_read_format_rar5_fileattr) { unsigned long set, clear, flag; diff --git a/libarchive/test/test_read_format_rar5_dirdata.rar.uu b/libarchive/test/test_read_format_rar5_dirdata.rar.uu new file mode 100644 index 000000000..c7928f344 --- /dev/null +++ b/libarchive/test/test_read_format_rar5_dirdata.rar.uu @@ -0,0 +1,6 @@ +begin 644 - +M4F%R(1H'`0`BD'[;,`$%,#8P`0&`@("``B?GD;$U`@(+@X``"_C5%:2#``(` +M`#"``S`P,#`P,#`P,#!);S#6KA',@]:N$?*IN;YV[8"1S>?4^`,#`R,#`P,# +-`P,#1)'C@XX*4`O.^P`` +` +end