]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Merge pull request #2716 from antekone/bug/GH-2714/infinite-loop/1
authorMartin Matuška <martin@matuska.de>
Thu, 14 Aug 2025 21:20:55 +0000 (23:20 +0200)
committerMartin Matuska <martin@matuska.de>
Tue, 23 Sep 2025 20:58:12 +0000 (22:58 +0200)
RAR5 reader: early fail when file declares data for a dir entry

(cherry picked from commit 2db13f74226f766ef776dbe74654bcd7120a19e8)

Makefile.am
libarchive/archive_read_support_format_rar5.c
libarchive/test/test_read_format_rar5.c
libarchive/test/test_read_format_rar5_dirdata.rar.uu [new file with mode: 0644]

index 9d8873d1d987715440fd943fc11c2b08f67fcc28..05ff1b7c4ca1e2b656584f966d39b9617006ca47 100644 (file)
@@ -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 \
index f2af82cfadd31b84fc328aef480b0d609c3c1145..17e501e02e9f1733c0c2f7abf4c6026db0f898a7 100644 (file)
@@ -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)) {
index 202b66f680082eab1adc729e906b585df04b2d64..6ab0d236a1eda891b8373c7851cd52175bae1a92 100644 (file)
@@ -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 (file)
index 0000000..c7928f3
--- /dev/null
@@ -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