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