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