9ae17e
From 6d351831be705cc26d897db44f878a978f4138fc Mon Sep 17 00:00:00 2001
9ae17e
From: Mark Adler <madler@alumni.caltech.edu>
9ae17e
Date: Thu, 25 Jul 2019 20:43:17 -0700
9ae17e
Subject: [PATCH] Do not raise a zip bomb alert for a misplaced central
9ae17e
 directory.
9ae17e
9ae17e
There is a zip-like file in the Firefox distribution, omni.ja,
9ae17e
which is a zip container with the central directory placed at the
9ae17e
start of the file instead of after the local entries as required
9ae17e
by the zip standard. This commit marks the actual location of the
9ae17e
central directory, as well as the end of central directory records,
9ae17e
as disallowed locations. This now permits such containers to not
9ae17e
raise a zip bomb alert, where in fact there are no overlaps.
9ae17e
---
9ae17e
 extract.c | 25 +++++++++++++++++++------
9ae17e
 process.c |  6 ++++++
9ae17e
 unzpriv.h | 10 ++++++++++
9ae17e
 3 files changed, 35 insertions(+), 6 deletions(-)
9ae17e
9ae17e
diff --git a/extract.c b/extract.c
9ae17e
index 0973a33..1b73cb0 100644
9ae17e
--- a/extract.c
9ae17e
+++ b/extract.c
9ae17e
@@ -493,8 +493,11 @@ int extract_or_test_files(__G)    /* return PK-type error code */
9ae17e
     }
9ae17e
 #endif /* !SFX || SFX_EXDIR */
9ae17e
 
9ae17e
-    /* One more: initialize cover structure for bomb detection. Start with a
9ae17e
-       span that covers the central directory though the end of the file. */
9ae17e
+    /* One more: initialize cover structure for bomb detection. Start with
9ae17e
+       spans that cover any extra bytes at the start, the central directory,
9ae17e
+       the end of central directory record (including the Zip64 end of central
9ae17e
+       directory locator, if present), and the Zip64 end of central directory
9ae17e
+       record, if present. */
9ae17e
     if (G.cover == NULL) {
9ae17e
         G.cover = malloc(sizeof(cover_t));
9ae17e
         if (G.cover == NULL) {
9ae17e
@@ -506,15 +509,25 @@ int extract_or_test_files(__G)    /* return PK-type error code */
9ae17e
         ((cover_t *)G.cover)->max = 0;
9ae17e
     }
9ae17e
     ((cover_t *)G.cover)->num = 0;
9ae17e
-    if ((G.extra_bytes != 0 &&
9ae17e
-         cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) ||
9ae17e
-        cover_add((cover_t *)G.cover,
9ae17e
+    if (cover_add((cover_t *)G.cover,
9ae17e
                   G.extra_bytes + G.ecrec.offset_start_central_directory,
9ae17e
-                  G.ziplen) != 0) {
9ae17e
+                  G.extra_bytes + G.ecrec.offset_start_central_directory +
9ae17e
+                  G.ecrec.size_central_directory) != 0) {
9ae17e
         Info(slide, 0x401, ((char *)slide,
9ae17e
           LoadFarString(NotEnoughMemCover)));
9ae17e
         return PK_MEM;
9ae17e
     }
9ae17e
+    if ((G.extra_bytes != 0 &&
9ae17e
+         cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) ||
9ae17e
+        (G.ecrec.have_ecr64 &&
9ae17e
+         cover_add((cover_t *)G.cover, G.ecrec.ec64_start,
9ae17e
+                   G.ecrec.ec64_end) != 0) ||
9ae17e
+        cover_add((cover_t *)G.cover, G.ecrec.ec_start,
9ae17e
+                  G.ecrec.ec_end) != 0) {
9ae17e
+        Info(slide, 0x401, ((char *)slide,
9ae17e
+          LoadFarString(OverlappedComponents)));
9ae17e
+        return PK_BOMB;
9ae17e
+    }
9ae17e
 
9ae17e
 /*---------------------------------------------------------------------------
9ae17e
     The basic idea of this function is as follows.  Since the central di-
9ae17e
diff --git a/process.c b/process.c
9ae17e
index d2e4dc3..d75d405 100644
9ae17e
--- a/process.c
9ae17e
+++ b/process.c
9ae17e
@@ -1408,6 +1408,10 @@ static int find_ecrec64(__G__ searchlen)         /* return PK-class error */
9ae17e
 
9ae17e
     /* Now, we are (almost) sure that we have a Zip64 archive. */
9ae17e
     G.ecrec.have_ecr64 = 1;
9ae17e
+    G.ecrec.ec_start -= ECLOC64_SIZE+4;
9ae17e
+    G.ecrec.ec64_start = ecrec64_start_offset;
9ae17e
+    G.ecrec.ec64_end = ecrec64_start_offset +
9ae17e
+                       12 + makeint64(&byterec[ECREC64_LENGTH]);
9ae17e
 
9ae17e
     /* Update the "end-of-central-dir offset" for later checks. */
9ae17e
     G.real_ecrec_offset = ecrec64_start_offset;
9ae17e
@@ -1542,6 +1546,8 @@ static int find_ecrec(__G__ searchlen)          /* return PK-class error */
9ae17e
       makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]);
9ae17e
     G.ecrec.zipfile_comment_length =
9ae17e
       makeword(&byterec[ZIPFILE_COMMENT_LENGTH]);
9ae17e
+    G.ecrec.ec_start = G.real_ecrec_offset;
9ae17e
+    G.ecrec.ec_end = G.ecrec.ec_start + 22 + G.ecrec.zipfile_comment_length;
9ae17e
 
9ae17e
     /* Now, we have to read the archive comment, BEFORE the file pointer
9ae17e
        is moved away backwards to seek for a Zip64 ECLOC64 structure.
9ae17e
diff --git a/unzpriv.h b/unzpriv.h
9ae17e
index dc9eff5..297b3c7 100644
9ae17e
--- a/unzpriv.h
9ae17e
+++ b/unzpriv.h
9ae17e
@@ -2185,6 +2185,16 @@ typedef struct VMStimbuf {
9ae17e
        int have_ecr64;                  /* valid Zip64 ecdir-record exists */
9ae17e
        int is_zip64_archive;            /* Zip64 ecdir-record is mandatory */
9ae17e
        ush zipfile_comment_length;
9ae17e
+       zusz_t ec_start, ec_end;         /* offsets of start and end of the
9ae17e
+                                           end of central directory record,
9ae17e
+                                           including if present the Zip64
9ae17e
+                                           end of central directory locator,
9ae17e
+                                           which immediately precedes the
9ae17e
+                                           end of central directory record */
9ae17e
+       zusz_t ec64_start, ec64_end;     /* if have_ecr64 is true, then these
9ae17e
+                                           are the offsets of the start and
9ae17e
+                                           end of the Zip64 end of central
9ae17e
+                                           directory record */
9ae17e
    } ecdir_rec;
9ae17e
 
9ae17e