]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
rar: limit and skip NEWSUB extended data 3015/head
authorCris Jacob Maamor <crisjacobmaamor@gmail.com>
Sat, 9 May 2026 19:31:37 +0000 (03:31 +0800)
committerCris Jacob Maamor <crisjacobmaamor@gmail.com>
Sat, 9 May 2026 19:31:37 +0000 (03:31 +0800)
Avoid requesting NEWSUB extended data through read-ahead while parsing the header. The full NEWSUB block size is still validated and consumed, but the extended data is not required to be present in memory during header parsing.

Add a test for a malformed NEWSUB header with a large packed size.

libarchive/archive_read_support_format_rar.c
libarchive/test/test_read_format_rar.c

index f930379d940d1c6aae7a01bbab3509fc4385bdda..3315c55a3fb8d441bfacfc5c3def0849e120ecb1 100644 (file)
 #define NS_UNIT 10000000
 
 #define DICTIONARY_MAX_SIZE 0x400000
+#define RAR_MAX_NEWSUB_SIZE (1024 * 1024)
 
 #define MAINCODE_SIZE      299
 #define OFFSETCODE_SIZE    60
@@ -1513,27 +1514,20 @@ read_header(struct archive_read *a, struct archive_entry *entry,
    * consumed at the end.
    */
   if (head_type == NEWSUB_HEAD) {
-    size_t distance = p - (const char *)h;
-    if (rar->packed_size > INT64_MAX - header_size) {
+    if (rar->packed_size > RAR_MAX_NEWSUB_SIZE ||
+        rar->packed_size > INT64_MAX - header_size) {
       archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-                        "Extended header size too large");
+                        "Invalid RAR file: Overlarge extended header");
       return (ARCHIVE_FATAL);
     }
-    header_size += rar->packed_size;
-    if ((uintmax_t)header_size > SIZE_MAX) {
+    if (__archive_read_consume(a, header_size + rar->packed_size - 7) < 0) {
       archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
-                        "Unable to read extended header data");
+                        "Invalid RAR file: Cannot 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) {
-      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;
+
+    /* NEWSUB blocks are metadata-only here; skip them without creating an entry. */
+    return ret;
   }
 
   filename_size = archive_le16dec(file_header.name_size);
@@ -1829,10 +1823,6 @@ read_header(struct archive_read *a, struct archive_entry *entry,
   rar->ppmd_valid = rar->ppmd_eod = 0;
   rar->filters.filterstart = INT64_MAX;
 
-  /* Don't set any archive entries for non-file header types */
-  if (head_type == NEWSUB_HEAD)
-    return ret;
-
   archive_entry_set_mtime(entry, rar->mtime, rar->mnsec);
   archive_entry_set_ctime(entry, rar->ctime, rar->cnsec);
   archive_entry_set_atime(entry, rar->atime, rar->ansec);
index 59e83846c863d584583d8fbce30bea615f63d0fd..61169bd3b9070da3620c923e47f6d951c30e10d1 100644 (file)
@@ -3906,6 +3906,35 @@ DEFINE_TEST(test_read_format_rar_newsub_huge)
 #endif
 }
 
+DEFINE_TEST(test_read_format_rar_newsub_huge64)
+{
+  static const unsigned char archive[] = {
+    'R', 'a', 'r', '!', 0x1a, 0x07, 0x00,
+    0x23, 0x25, 0x7a, 0x00, 0x01, 0x28, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+    0x7f, 0x00, 0x00, 0x00, 0x00
+  };
+  struct archive_entry *ae;
+  struct archive *a;
+
+  assert((a = archive_read_new()) != NULL);
+  assertA(0 == archive_read_support_filter_all(a));
+  assertA(0 == archive_read_support_format_rar(a));
+  assertA(0 == archive_read_open_memory(a, archive, sizeof(archive)));
+
+  failure("Malformed RAR NEWSUB header should be rejected before "
+          "extended data read-ahead");
+  assertEqualIntA(a, ARCHIVE_FATAL, archive_read_next_header(a, &ae));
+  assertEqualString("Invalid RAR file: Overlarge extended header",
+                    archive_error_string(a));
+
+  assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+  assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
 DEFINE_TEST(test_read_format_rar_symlink_huge)
 {
 #if SIZE_MAX == UINT64_MAX