Subject: Do not raise a zip bomb alert for a misplaced central directory.
From: Mark Adler <[email-masked]>

Do not raise a zip bomb alert for a misplaced central directory.
    
    There is a zip-like file in the Firefox distribution, omni.ja,
    which is a zip container with the central directory placed at the
    start of the file instead of after the local entries as required
    by the zip standard. This commit marks the actual location of the
    central directory, as well as the end of central directory records,
    as disallowed locations. This now permits such containers to not
    raise a zip bomb alert, where in fact there are no overlaps.

--- a/extract.c
+++ b/extract.c
@@ -495,8 +495,11 @@
     }
 #endif /* !SFX || SFX_EXDIR */
 
-    /* One more: initialize cover structure for bomb detection. Start with a
-       span that covers the central directory though the end of the file. */
+    /* One more: initialize cover structure for bomb detection. Start with
+       spans that cover any extra bytes at the start, the central directory,
+       the end of central directory record (including the Zip64 end of central
+       directory locator, if present), and the Zip64 end of central directory
+       record, if present. */
     if (G.cover == NULL) {
         G.cover = malloc(sizeof(cover_t));
         if (G.cover == NULL) {
@@ -508,15 +511,25 @@
         ((cover_t *)G.cover)->max = 0;
     }
     ((cover_t *)G.cover)->num = 0;
-    if ((G.extra_bytes != 0 &&
-         cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) ||
-        cover_add((cover_t *)G.cover,
+    if (cover_add((cover_t *)G.cover,
                   G.extra_bytes + G.ecrec.offset_start_central_directory,
-                  G.ziplen) != 0) {
+                  G.extra_bytes + G.ecrec.offset_start_central_directory +
+                  G.ecrec.size_central_directory) != 0) {
         Info(slide, 0x401, ((char *)slide,
           LoadFarString(NotEnoughMemCover)));
         return PK_MEM;
     }
+    if ((G.extra_bytes != 0 &&
+         cover_add((cover_t *)G.cover, 0, G.extra_bytes) != 0) ||
+        (G.ecrec.have_ecr64 &&
+         cover_add((cover_t *)G.cover, G.ecrec.ec64_start,
+                   G.ecrec.ec64_end) != 0) ||
+        cover_add((cover_t *)G.cover, G.ecrec.ec_start,
+                  G.ecrec.ec_end) != 0) {
+        Info(slide, 0x401, ((char *)slide,
+          LoadFarString(OverlappedComponents)));
+        return PK_BOMB;
+    }
 
 /*---------------------------------------------------------------------------
     The basic idea of this function is as follows.  Since the central di-
--- a/process.c
+++ b/process.c
@@ -1408,6 +1408,10 @@
 
     /* Now, we are (almost) sure that we have a Zip64 archive. */
     G.ecrec.have_ecr64 = 1;
+    G.ecrec.ec_start -= ECLOC64_SIZE+4;
+    G.ecrec.ec64_start = ecrec64_start_offset;
+    G.ecrec.ec64_end = ecrec64_start_offset +
+                       12 + makeint64(&byterec[ECREC64_LENGTH]);
 
     /* Update the "end-of-central-dir offset" for later checks. */
     G.real_ecrec_offset = ecrec64_start_offset;
@@ -1542,6 +1546,8 @@
       makelong(&byterec[OFFSET_START_CENTRAL_DIRECTORY]);
     G.ecrec.zipfile_comment_length =
       makeword(&byterec[ZIPFILE_COMMENT_LENGTH]);
+    G.ecrec.ec_start = G.real_ecrec_offset;
+    G.ecrec.ec_end = G.ecrec.ec_start + 22 + G.ecrec.zipfile_comment_length;
 
     /* Now, we have to read the archive comment, BEFORE the file pointer
        is moved away backwards to seek for a Zip64 ECLOC64 structure.
--- a/unzpriv.h
+++ b/unzpriv.h
@@ -2185,6 +2185,16 @@
        int have_ecr64;                  /* valid Zip64 ecdir-record exists */
        int is_zip64_archive;            /* Zip64 ecdir-record is mandatory */
        ush zipfile_comment_length;
+       zusz_t ec_start, ec_end;         /* offsets of start and end of the
+                                           end of central directory record,
+                                           including if present the Zip64
+                                           end of central directory locator,
+                                           which immediately precedes the
+                                           end of central directory record */
+       zusz_t ec64_start, ec64_end;     /* if have_ecr64 is true, then these
+                                           are the offsets of the start and
+                                           end of the Zip64 end of central
+                                           directory record */
    } ecdir_rec;