]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
rar: Check packed_size constraints (#2591)
authorTobias Stoeckmann <stoeckmann@users.noreply.github.com>
Fri, 9 May 2025 11:31:24 +0000 (13:31 +0200)
committerGitHub <noreply@github.com>
Fri, 9 May 2025 11:31:24 +0000 (13:31 +0200)
Make sure that size_t casts do not truncate the value of packed_size on
32 bit systems since it's 64 bit. Extensions to RAR format allow 64 bit
values to be specified in archives.

Also verify that 64 bit signed arithmetics do not overflow.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
Makefile.am
libarchive/archive_read_support_format_rar.c
libarchive/test/test_read_format_rar.c
libarchive/test/test_read_format_rar_newsub_huge.rar.uu [new file with mode: 0644]
libarchive/test/test_read_format_rar_symlink_huge.rar.uu [new file with mode: 0644]

index 06ee9bb42204d6a860b01e58362df3a7aa364509..c6d072cf318e46791557b4d7c08300170af31166 100644 (file)
@@ -896,12 +896,14 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_read_format_rar_multivolume.part0002.rar.uu \
        libarchive/test/test_read_format_rar_multivolume.part0003.rar.uu \
        libarchive/test/test_read_format_rar_multivolume.part0004.rar.uu \
+       libarchive/test/test_read_format_rar_newsub_huge.rar.uu \
        libarchive/test/test_read_format_rar_noeof.rar.uu \
        libarchive/test/test_read_format_rar_ppmd_lzss_conversion.rar.uu \
        libarchive/test/test_read_format_rar_ppmd_use_after_free.rar.uu \
        libarchive/test/test_read_format_rar_ppmd_use_after_free2.rar.uu \
        libarchive/test/test_read_format_rar_sfx.exe.uu \
        libarchive/test/test_read_format_rar_subblock.rar.uu \
+       libarchive/test/test_read_format_rar_symlink_huge.rar.uu \
        libarchive/test/test_read_format_rar_unicode.rar.uu \
        libarchive/test/test_read_format_rar_windows.rar.uu \
        libarchive/test/test_read_format_rar4_encrypted.rar.uu \
index 8730f0771b136a066d7527ef2f4cca91624a2c0e..542532dd7bcf6eb37d69137801c50e028c214785 100644 (file)
@@ -953,8 +953,11 @@ archive_read_format_rar_read_header(struct archive_read *a,
   {
     unsigned long crc32_val;
 
-    if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+    if ((h = __archive_read_ahead(a, 7, NULL)) == NULL) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Failed to read next header.");
       return (ARCHIVE_FATAL);
+    }
     p = h;
 
     head_type = p[2];
@@ -1436,7 +1439,11 @@ read_header(struct archive_read *a, struct archive_entry *entry,
   }
 
   if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Failed to read full header content.");
     return (ARCHIVE_FATAL);
+  }
 
   /* File Header CRC check. */
   crc32_computed = crc32(crc32_computed, h, (unsigned)(header_size - 7));
@@ -1509,10 +1516,23 @@ read_header(struct archive_read *a, struct archive_entry *entry,
    */
   if (head_type == NEWSUB_HEAD) {
     size_t distance = p - (const char *)h;
+    if (rar->packed_size > INT64_MAX - header_size) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Extended header size too large.");
+      return (ARCHIVE_FATAL);
+    }
     header_size += rar->packed_size;
+    if ((uintmax_t)header_size > SIZE_MAX) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Unable to read extended header data.");
+      return (ARCHIVE_FATAL);
+    }
     /* Make sure we have the extended data. */
-    if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
-        return (ARCHIVE_FATAL);
+    if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Failed to read extended header data.");
+      return (ARCHIVE_FATAL);
+    }
     p = h;
     endp = p + header_size - 7;
     p += distance;
@@ -1694,6 +1714,12 @@ read_header(struct archive_read *a, struct archive_entry *entry,
     }
     if (rar->dbo[rar->cursor].start_offset < 0)
     {
+      if (rar->packed_size > INT64_MAX - a->filter->position)
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Unable to store offsets.");
+        return (ARCHIVE_FATAL);
+      }
       rar->dbo[rar->cursor].start_offset = a->filter->position;
       rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset +
         rar->packed_size;
@@ -1750,6 +1776,11 @@ read_header(struct archive_read *a, struct archive_entry *entry,
   }
 
   __archive_read_consume(a, header_size - 7);
+  if (rar->packed_size > INT64_MAX - a->filter->position) {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Unable to store offsets.");
+    return (ARCHIVE_FATAL);
+  }
   rar->dbo[0].start_offset = a->filter->position;
   rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size;
 
@@ -1947,8 +1978,18 @@ read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
   int ret = (ARCHIVE_OK);
 
   rar = (struct rar *)(a->format->data);
+  if ((uintmax_t)rar->packed_size > SIZE_MAX)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Unable to read link.");
+    return (ARCHIVE_FATAL);
+  }
   if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Failed to read link.");
     return (ARCHIVE_FATAL);
+  }
   p = h;
 
   if (archive_entry_copy_symlink_l(entry,
index e6ce8067e0661aa0c7d056fa590665b0ee420848..59e83846c863d584583d8fbce30bea615f63d0fd 100644 (file)
@@ -3881,3 +3881,51 @@ DEFINE_TEST(test_read_format_rar_ppmd_use_after_free2)
   assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
   assertEqualInt(ARCHIVE_OK, archive_read_free(a));
 }
+
+DEFINE_TEST(test_read_format_rar_newsub_huge)
+{
+#if SIZE_MAX == UINT64_MAX
+  skipping("not relevant on 64 bit platforms");
+#else
+  const char* reffile = "test_read_format_rar_newsub_huge.rar";
+
+  struct archive_entry *ae;
+  struct archive *a;
+
+  extract_reference_file(reffile);
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_support_filter_all(a));
+  assertA(0 == archive_read_support_format_all(a));
+  assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+  /* Test for truncation */
+  assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+
+  assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+#endif
+}
+
+DEFINE_TEST(test_read_format_rar_symlink_huge)
+{
+#if SIZE_MAX == UINT64_MAX
+  skipping("not relevant on 64 bit platforms");
+#else
+  const char* reffile = "test_read_format_rar_symlink_huge.rar";
+
+  struct archive_entry *ae;
+  struct archive *a;
+
+  extract_reference_file(reffile);
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_support_filter_all(a));
+  assertA(0 == archive_read_support_format_all(a));
+  assertA(0 == archive_read_open_filename(a, reffile, 10240));
+
+  /* Test for invalid entry */
+  assertA(ARCHIVE_FATAL == archive_read_next_header(a, &ae));
+
+  assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+#endif
+}
diff --git a/libarchive/test/test_read_format_rar_newsub_huge.rar.uu b/libarchive/test/test_read_format_rar_newsub_huge.rar.uu
new file mode 100644 (file)
index 0000000..b2255b1
--- /dev/null
@@ -0,0 +1,5 @@
+begin 644 test_read_format_rar_newsub_huge.rar.uu
+M4F%R(1H'`",E>@`!*```````````````````````````````````____?P``
+"````
+`
+end
diff --git a/libarchive/test/test_read_format_rar_symlink_huge.rar.uu b/libarchive/test/test_read_format_rar_symlink_huge.rar.uu
new file mode 100644 (file)
index 0000000..7827e1b
--- /dev/null
@@ -0,0 +1,5 @@
+begin 644 test_read_format_rar_symlink_huge.rar
+M4F%R(1H'`'6E=``!*0````````````,``````````````0``H```____?P``
+#``!X
+`
+end