[packages/unzip] - rel 7; SECURITY: CVE-2021-4217, CVE-2022-0529, CVE-2022-0530

arekm arekm at pld-linux.org
Sun Aug 16 22:37:51 CEST 2026


commit 6a7f58c9608e77b3db1a6a5b5f1861f924ac14c2
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Sun Aug 16 22:37:12 2026 +0200

    - rel 7; SECURITY: CVE-2021-4217, CVE-2022-0529, CVE-2022-0530
    
    Syncs the patch stack with Fedora 6.0-71

 unzip-6.0-CVE-2022-0529-and-0530.patch            | 170 +++++++++++++++++++++
 unzip-6.0-RHEL-86228.patch                        |  19 +++
 unzip-6.0-alt-iconv-utf8.patch                    |  12 +-
 unzip-6.0-cve-2021-4217.patch                     |  39 +++++
 unzip-6.0-fix-warning-messages-on-big-files.patch |  15 ++
 unzip-6.0-sast.patch                              |  11 ++
 unzip-6.0-wcstombs-fortify.patch                  |  11 ++
 unzip-zipbomb-part7.patch                         | 172 ++++++++++++++++++++++
 unzip-zipbomb-switch.patch                        |  27 +---
 unzip.spec                                        |  51 +++++--
 10 files changed, 487 insertions(+), 40 deletions(-)
---
diff --git a/unzip.spec b/unzip.spec
index ee95d93..174e61c 100644
--- a/unzip.spec
+++ b/unzip.spec
@@ -1,6 +1,3 @@
-#
-# TODO:		bzip2 support
-#
 Summary:	Unpacks .zip files such as those made by pkzip under DOS
 Summary(de.UTF-8):	entpackt .zip-Dateien (etwa mit pkzip unter DOS erstellte)
 Summary(es.UTF-8):	Descompacta archivos con extensión .zip, como los que crea pkzip en DOS
@@ -13,12 +10,11 @@ Summary(tr.UTF-8):	pkzip ve benzeri programların ürettiği zip arşivlerini a
 Summary(uk.UTF-8):	Розпаковувач файлів .zip
 Name:		unzip
 Version:	6.00
-Release:	6
+Release:	7
 License:	distributable
 Group:		Applications/Archiving
-Source0:	ftp://ftp.info-zip.org/pub/infozip/src/%{name}60.tgz
+Source0:	https://downloads.sourceforge.net/project/infozip/UnZip%206.x%20(latest)/UnZip%206.0/%{name}60.tar.gz?/%{name}60.tgz
 # Source0-md5:	62b490407489521db863b523a7f86375
-#Source0:	ftp://sunsite.icm.edu.pl/pub/unix/archiving/info-zip/src/%{name}552.tar.gz
 Source1:	http://www.mif.pg.gda.pl/homepages/ankry/man-PLD/%{name}-non-english-man-pages.tar.bz2
 # Source1-md5:	d7f8b0b09f6e8d89591b4dc25e335764
 Patch100:	unzip-opt.patch
@@ -82,11 +78,28 @@ Patch29: unzip-zipbomb-manpage.patch
 Patch30: unzip-zipbomb-part4.patch
 Patch31: unzip-zipbomb-part5.patch
 Patch32: unzip-zipbomb-part6.patch
-Patch33: unzip-zipbomb-switch.patch
-URL:		http://www.info-zip.org/
-BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
+Patch33: unzip-zipbomb-part7.patch
+Patch34: unzip-zipbomb-switch.patch
+
+# fix _FORTIFY_SOURCE abort in fnfilter()
+Patch35: unzip-6.0-wcstombs-fortify.patch
+
+# dropped byte 5 when decoding 64-bit sizes
+Patch36: unzip-6.0-fix-warning-messages-on-big-files.patch
 
-%define		Werror_cflags	%{nil}
+# strcpy() on overlapping buffers in envargs()
+Patch37: unzip-6.0-sast.patch
+
+# ignore the bogus "total number of disks" Microsoft writes into ZIP64 archives
+Patch38: unzip-6.0-RHEL-86228.patch
+
+Patch39: unzip-6.0-CVE-2022-0529-and-0530.patch
+Patch40: unzip-6.0-cve-2021-4217.patch
+URL:		https://infozip.sourceforge.net/
+# unix/configure probes for it; without the BR the resulting binary silently
+# loses bzip2 (method 12) support
+BuildRequires:	bzip2-devel
+BuildRoot:	%{tmpdir}/%{name}-%{version}-root-%(id -u -n)
 
 %description
 unzip will list, test, or extract files from a ZIP archive, commonly
@@ -191,6 +204,13 @@ PKZIP та PKUNZIP від PKWARE для MS-DOS, але в багатьох ви
 %patch -P31 -p1
 %patch -P32 -p1
 %patch -P33 -p1
+%patch -P34 -p1
+%patch -P35 -p1
+%patch -P36 -p1
+%patch -P37 -p1
+%patch -P38 -p1
+%patch -P39 -p1
+%patch -P40 -p1
 
 %build
 # IZ_HAVE_UXUIDGID is needed for right functionality of unzip -X
@@ -198,6 +218,9 @@ PKZIP та PKUNZIP від PKWARE для MS-DOS, але в багатьох ви
 # but successfull.
 #
 # NOTE: unix/configure creates flags file with guessed values
+#
+# -std=gnu89 must stay on CC, not CF_NOOPT: the probes compile with bare $CC
+# and silently misdetect fchown/lchown/opendir as missing without it
 X86_OPT1=""
 X86_OPT2=""
 %ifarch %{ix86}
@@ -206,10 +229,10 @@ X86_OPT2="-DASM_CRC"
 %endif
 
 %{__make} -f unix/Makefile generic \
-	CC="%{__cc}" \
-	AS="%{__cc}" \
-	CF_NOOPT="%{rpmcppflags} %{rpmcflags} -I. -Wall ${X86_OPT2} -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DLARGE_FILE_SUPPORT -DUNICODE_SUPPORT -DUNICODE_WCHAR -DUNICODE_SUPPORT -DUTF8_MAYBE_NATIVE -DNO_LCHMOD -DHAVE_DIRENT_H -DHAVE_TERMIOS_H -D_MBCS -DNOMEMCPY -DIZ_HAVE_UXUIDGID" \
-	AF="${X86_OPT} %{rpmldflags}" \
+	CC="%{__cc} -std=gnu89" \
+	AS="%{__cc} -std=gnu89" \
+	CF_NOOPT="%{rpmcppflags} %{rpmcflags} -I. -Wall ${X86_OPT2} -DLARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DLARGE_FILE_SUPPORT -DUNICODE_SUPPORT -DUNICODE_WCHAR -DUTF8_MAYBE_NATIVE -DNO_LCHMOD -DHAVE_DIRENT_H -DHAVE_TERMIOS_H -D_MBCS -DNOMEMCPY -DIZ_HAVE_UXUIDGID" \
+	AF="${X86_OPT1}" \
 %ifarch %{ix86}
 	CRCA_O="crc_gcc.o" \
 %endif
diff --git a/unzip-6.0-CVE-2022-0529-and-0530.patch b/unzip-6.0-CVE-2022-0529-and-0530.patch
new file mode 100644
index 0000000..2b84b96
--- /dev/null
+++ b/unzip-6.0-CVE-2022-0529-and-0530.patch
@@ -0,0 +1,170 @@
+From: Steven M. Schweda <sms at antinode.info>
+Subject: Fix for CVE-2022-0529 and CVE-2022-0530
+Bug-Debian: https://bugs.debian.org/1010355
+X-Debian-version: 6.0-27
+
+--- a/fileio.c
++++ b/fileio.c
+@@ -171,8 +171,10 @@
+ static ZCONST char Far FilenameTooLongTrunc[] =
+   "warning:  filename too long--truncating.\n";
+ #ifdef UNICODE_SUPPORT
++   static ZCONST char Far UFilenameCorrupt[] =
++     "error: Unicode filename corrupt.\n";
+    static ZCONST char Far UFilenameTooLongTrunc[] =
+-     "warning:  Converted unicode filename too long--truncating.\n";
++     "warning:  Converted Unicode filename too long--truncating.\n";
+ #endif
+ static ZCONST char Far ExtraFieldTooLong[] =
+   "warning:  extra field too long (%d).  Ignoring...\n";
+@@ -2361,16 +2363,30 @@
+                   /* convert UTF-8 to local character set */
+                   fn = utf8_to_local_string(G.unipath_filename,
+                                             G.unicode_escape_all);
+-                  /* make sure filename is short enough */
+-                  if (strlen(fn) >= FILNAMSIZ) {
+-                    fn[FILNAMSIZ - 1] = '\0';
++
++                  /* 2022-07-22 SMS, et al.  CVE-2022-0530
++                   * Detect conversion failure, emit message.
++                   * Continue with unconverted name.
++                   */
++                  if (fn == NULL)
++                  {
+                     Info(slide, 0x401, ((char *)slide,
+-                      LoadFarString(UFilenameTooLongTrunc)));
+-                    error = PK_WARN;
++                     LoadFarString(UFilenameCorrupt)));
++                    error = PK_ERR;
++                  }
++                  else
++                  {
++                    /* make sure filename is short enough */
++                    if (strlen(fn) >= FILNAMSIZ) {
++                      fn[FILNAMSIZ - 1] = '\0';
++                      Info(slide, 0x401, ((char *)slide,
++                        LoadFarString(UFilenameTooLongTrunc)));
++                      error = PK_WARN;
++                    }
++                    /* replace filename with converted UTF-8 */
++                    strcpy(G.filename, fn);
++                    free(fn);
+                   }
+-                  /* replace filename with converted UTF-8 */
+-                  strcpy(G.filename, fn);
+-                  free(fn);
+                 }
+ # endif /* UNICODE_WCHAR */
+                 if (G.unipath_filename != G.filename_full)
+--- a/process.c
++++ b/process.c
+@@ -222,6 +222,8 @@
+      "\nwarning:  Unicode Path version > 1\n";
+    static ZCONST char Far UnicodeMismatchError[] =
+      "\nwarning:  Unicode Path checksum invalid\n";
++   static ZCONST char Far UFilenameTooLongTrunc[] =
++     "warning:  filename too long (P1) -- truncating.\n";
+ #endif
+ 
+ 
+@@ -1915,7 +1917,7 @@
+     Sets both local header and central header fields.  Not terribly clever,
+     but it means that this procedure is only called in one place.
+ 
+-    2014-12-05 SMS.
++    2014-12-05 SMS.  (oCERT.org report.)  CVE-2014-8141.
+     Added checks to ensure that enough data are available before calling
+     makeint64() or makelong().  Replaced various sizeof() values with
+     simple ("4" or "8") constants.  (The Zip64 structures do not depend
+@@ -1947,7 +1949,7 @@
+ 
+         if (eb_id == EF_PKSZ64)
+         {
+-          int offset = EB_HEADSIZE;
++          unsigned offset = EB_HEADSIZE;
+ 
+           if ((G.crec.ucsize == Z64FLGL) || (G.lrec.ucsize == Z64FLGL))
+           {
+@@ -2046,7 +2049,7 @@
+         }
+         if (eb_id == EF_UNIPATH) {
+ 
+-          int offset = EB_HEADSIZE;
++          unsigned offset = EB_HEADSIZE;
+           ush ULen = eb_len - 5;
+           ulg chksum = CRCVAL_INITIAL;
+ 
+@@ -2504,16 +2507,17 @@
+   int state_dependent;
+   int wsize = 0;
+   int max_bytes = MB_CUR_MAX;
+-  char buf[9];
++  char buf[ MB_CUR_MAX+ 1];             /* ("+1" not really needed?) */
+   char *buffer = NULL;
+   char *local_string = NULL;
++  size_t buffer_size;                   /* CVE-2022-0529 */
+ 
+   for (wsize = 0; wide_string[wsize]; wsize++) ;
+ 
+   if (max_bytes < MAX_ESCAPE_BYTES)
+     max_bytes = MAX_ESCAPE_BYTES;
+-
+-  if ((buffer = (char *)malloc(wsize * max_bytes + 1)) == NULL) {
++  buffer_size = wsize * max_bytes + 1;          /* Reused below. */
++  if ((buffer = (char *)malloc( buffer_size)) == NULL) {
+     return NULL;
+   }
+ 
+@@ -2551,8 +2555,28 @@
+     } else {
+       /* no MB for this wide */
+         /* use escape for wide character */
+-        char *escape_string = wide_to_escape_string(wide_string[i]);
+-        strcat(buffer, escape_string);
++        size_t buffer_len;
++        size_t escape_string_len;
++        char *escape_string;
++        int err_msg = 0;
++
++        escape_string = wide_to_escape_string(wide_string[i]);
++        buffer_len = strlen( buffer);
++        escape_string_len = strlen( escape_string);
++
++        /* Append escape string, as space allows. */
++        /* 2022-07-18 SMS, et al.  CVE-2022-0529 */
++        if (escape_string_len > buffer_size- buffer_len- 1)
++        {
++            escape_string_len = buffer_size- buffer_len- 1;
++            if (err_msg == 0)
++            {
++                err_msg = 1;
++                Info(slide, 0x401, ((char *)slide,
++                 LoadFarString( UFilenameTooLongTrunc)));
++            }
++        }
++        strncat( buffer, escape_string, escape_string_len);
+         free(escape_string);
+     }
+   }
+@@ -2604,9 +2628,18 @@
+   ZCONST char *utf8_string;
+   int escape_all;
+ {
+-  zwchar *wide = utf8_to_wide_string(utf8_string);
+-  char *loc = wide_to_local_string(wide, escape_all);
+-  free(wide);
++  zwchar *wide;
++  char *loc = NULL;
++
++  wide = utf8_to_wide_string( utf8_string);
++
++  /* 2022-07-25 SMS, et al.  CVE-2022-0530 */
++  if (wide != NULL)
++  {
++    loc = wide_to_local_string( wide, escape_all);
++    free( wide);
++  }
++
+   return loc;
+ }
+ 
diff --git a/unzip-6.0-RHEL-86228.patch b/unzip-6.0-RHEL-86228.patch
new file mode 100644
index 0000000..25c2fbb
--- /dev/null
+++ b/unzip-6.0-RHEL-86228.patch
@@ -0,0 +1,19 @@
+From: Roy Tam
+Subject: Handle Microsoft ZIP64 files by ignoring invalid "Total number of disks" field
+Origin: https://sourceforge.net/p/infozip/bugs/42/
+Bug: https://sourceforge.net/p/infozip/bugs/42/
+Bug-Debian: https://bugs.debian.org/1064000
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/unzip/+bug/2051952
+X-Debian-version: 6.0-29
+
+--- a/process.c
++++ b/process.c
+@@ -1281,7 +1281,7 @@
+     fprintf(stdout,"\nnumber of disks (ECR) %u, (ECLOC64) %lu\n",
+             G.ecrec.number_this_disk, ecloc64_total_disks); fflush(stdout);
+ #endif
+-    if ((G.ecrec.number_this_disk != 0xFFFF) &&
++    if ((G.ecrec.number_this_disk != 0xFFFF) && ecloc64_total_disks &&
+         (G.ecrec.number_this_disk != ecloc64_total_disks - 1)) {
+       /* Note: For some unknown reason, the developers at PKWARE decided to
+          store the "zip64 total disks" value as a counter starting from 1,
diff --git a/unzip-6.0-alt-iconv-utf8.patch b/unzip-6.0-alt-iconv-utf8.patch
index b9e3777..1db3164 100644
--- a/unzip-6.0-alt-iconv-utf8.patch
+++ b/unzip-6.0-alt-iconv-utf8.patch
@@ -174,11 +174,11 @@ Index: unzip-6.0/unzip.c
 +#else /* UNIX */
 +static ZCONST char Far ZipInfoUsageLine3[] = "miscellaneous options:\n\
 +  -h  print header line       -t  print totals for listed files or for all\n\
-+  -z  print zipfile comment  %c-T%c print file times in sortable decimal format\
-+\n %c-C%c be case-insensitive   %s\
++  -z  print zipfile comment   -T  print file times in sortable decimal format\
++\n  -C  be case-insensitive   %s\
 +  -x  exclude filenames that follow from listing\n\
-+  -O CHARSET  specify a character encoding for DOS, Windows and OS/2 archives\n\
-+  -I CHARSET  specify a character encoding for UNIX and other archives\n";
++  -O  CHARSET  specify a character encoding for DOS, Windows and OS/2 archives\n\
++  -I  CHARSET  specify a character encoding for UNIX and other archives\n";
 +#endif /* !UNIX */
  #ifdef MORE
     static ZCONST char Far ZipInfoUsageLine4[] =
@@ -196,8 +196,8 @@ Index: unzip-6.0/unzip.c
 +  -U  use escapes for all non-ASCII Unicode  -UU ignore any Unicode fields\n\
 +  -C  match filenames case-insensitively     -L  make (some) names \
 +lowercase\n %-42s  -V  retain VMS version numbers\n%s\
-+  -O CHARSET  specify a character encoding for DOS, Windows and OS/2 archives\n\
-+  -I CHARSET  specify a character encoding for UNIX and other archives\n\n";
++  -O  CHARSET  specify a character encoding for DOS, Windows and OS/2 archives\n\
++  -I  CHARSET  specify a character encoding for UNIX and other archives\n\n";
  #else /* !VMS */
  static ZCONST char Far UnzipUsageLine4[] = "\
  modifiers:\n\
diff --git a/unzip-6.0-cve-2021-4217.patch b/unzip-6.0-cve-2021-4217.patch
new file mode 100644
index 0000000..df2391e
--- /dev/null
+++ b/unzip-6.0-cve-2021-4217.patch
@@ -0,0 +1,39 @@
+From 731d698377dbd1f5b1b90efeb8094602ed59fc40 Mon Sep 17 00:00:00 2001
+From: Nils Bars <nils.bars at t-online.de>
+Date: Mon, 17 Jan 2022 16:53:16 +0000
+Subject: [PATCH] Fix null pointer dereference and use of uninitialized data
+
+This fixes a bug that causes use of uninitialized heap data if `readbuf` fails
+to read as many bytes as indicated by the extra field length attribute.
+Furthermore, this fixes a null pointer dereference if an archive contains an
+`EF_UNIPATH` extra field but does not have a filename set.
+
+The do_string()/fileio.c half is dropped here: unzip-6.0-valgrind.patch
+already zero-fills and truncates the short read.
+---
+ process.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/process.c b/process.c
+index d2a846e..cba2463 100644
+--- a/process.c
++++ b/process.c
+@@ -2064,10 +2064,14 @@ int getUnicodeData(__G__ ef_buf, ef_len)
+           G.unipath_checksum = makelong(offset + ef_buf);
+           offset += 4;
+ 
++          if (!G.filename_full) {
++            /* Check if we have a unicode extra section but no filename set */
++            return PK_ERR;
++          }
++
+           /*
+            * Compute 32-bit crc
+            */
+-
+           chksum = crc32(chksum, (uch *)(G.filename_full),
+                          strlen(G.filename_full));
+ 
+-- 
+2.25.1
+
diff --git a/unzip-6.0-fix-warning-messages-on-big-files.patch b/unzip-6.0-fix-warning-messages-on-big-files.patch
new file mode 100644
index 0000000..55a115a
--- /dev/null
+++ b/unzip-6.0-fix-warning-messages-on-big-files.patch
@@ -0,0 +1,15 @@
+From: "Steven M. Schweda" <sms at antinode.info>
+Subject: Fix lame code in fileio.c
+Bug-Debian: https://bugs.debian.org/929502
+X-Debian-version: 6.0-23
+
+--- a/fileio.c
++++ b/fileio.c
+@@ -2477,6 +2477,7 @@
+      */
+     return (((zusz_t)sig[7]) << 56)
+         + (((zusz_t)sig[6]) << 48)
++        + (((zusz_t)sig[5]) << 40)
+         + (((zusz_t)sig[4]) << 32)
+         + (zusz_t)((((ulg)sig[3]) << 24)
+                  + (((ulg)sig[2]) << 16)
diff --git a/unzip-6.0-sast.patch b/unzip-6.0-sast.patch
new file mode 100644
index 0000000..71b7cb9
--- /dev/null
+++ b/unzip-6.0-sast.patch
@@ -0,0 +1,11 @@
+--- a/envargs.c	2005-03-04 03:23:38.000000000 +0100
++++ b/envargs.c	2024-11-26 13:17:22.289650230 +0100
+@@ -118,7 +118,7 @@
+ 
+             /* remove escape characters */
+             while ((argstart = MBSCHR(argstart, '\\')) != (char *)NULL) {
+-                strcpy(argstart, argstart + 1);
++                memmove(argstart, argstart + 1, strlen(argstart + 1) + 1);
+                 if (*argstart)
+                     ++argstart;
+             }
diff --git a/unzip-6.0-wcstombs-fortify.patch b/unzip-6.0-wcstombs-fortify.patch
new file mode 100644
index 0000000..6e03cea
--- /dev/null
+++ b/unzip-6.0-wcstombs-fortify.patch
@@ -0,0 +1,11 @@
+--- unzip60/extract.c	2023-01-25 07:05:58.742254870 -0500
++++ unzip60.new/extract.c	2023-01-25 07:04:48.073435349 -0500
+@@ -2889,7 +2889,7 @@ char *fnfilter(raw, space, size)   /* co
+             strcpy( (char *)space, raw);
+             return (char *)space;
+         }
+-        woslen = wcstombs( newraw, wostring, (woslen * MB_CUR_MAX) + 1);
++        woslen = wcstombs( newraw, wostring, woslen + 1);
+ 
+         if (size > 0) {
+             slim = space + size - 4;
diff --git a/unzip-zipbomb-part7.patch b/unzip-zipbomb-part7.patch
new file mode 100644
index 0000000..4edc152
--- /dev/null
+++ b/unzip-zipbomb-part7.patch
@@ -0,0 +1,172 @@
+From af0d07f95809653b669d88aa0f424c6d5aa48ba0 Mon Sep 17 00:00:00 2001
+From: Mark Adler <fork at madler.net>
+Date: Sat, 2 Jul 2022 14:35:04 -0700
+Subject: [PATCH] Be more liberal in the acceptance of data descriptors.
+
+Previously the zip64 flag determined the size of the lengths in the
+data descriptor. This is compliant with the zip format. However, a
+bug in the Java zip library results in an incorrect setting of that
+flag. This commit permits either 32-bit or 64-bit lengths, auto-
+detecting which it is, which works around the Java bug.
+---
+ extract.c | 146 +++++++++++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 123 insertions(+), 23 deletions(-)
+
+diff --git a/extract.c b/extract.c
+index 878817d..b1c74df 100644
+--- a/extract.c
++++ b/extract.c
+@@ -2173,30 +2173,130 @@ static int extract_or_test_member(__G)    /* return PK-type error code */
+     undefer_input(__G);
+ 
+     if ((G.lrec.general_purpose_bit_flag & 8) != 0) {
+-        /* skip over data descriptor (harder than it sounds, due to signature
+-         * ambiguity)
+-         */
+-#       define SIG 0x08074b50
+-#       define LOW 0xffffffff
+-        uch buf[12];
+-        unsigned shy = 12 - readbuf((char *)buf, 12);
+-        ulg crc = shy ? 0 : makelong(buf);
+-        ulg clen = shy ? 0 : makelong(buf + 4);
+-        ulg ulen = shy ? 0 : makelong(buf + 8); /* or high clen if ZIP64 */
+-        if (crc == SIG &&                       /* if not SIG, no signature */
+-            (G.lrec.crc32 != SIG ||             /* if not SIG, have signature */
+-             (clen == SIG &&                    /* if not SIG, no signature */
+-              ((G.lrec.csize & LOW) != SIG ||   /* if not SIG, have signature */
+-               (ulen == SIG &&                  /* if not SIG, no signature */
+-                (G.pInfo->zip64 ? G.lrec.csize >> 32 : G.lrec.ucsize) != SIG
+-                                                /* if not SIG, have signature */
+-                )))))
+-                   /* skip four more bytes to account for signature */
+-                   shy += 4 - readbuf((char *)buf, 4);
+-        if (G.pInfo->zip64)
+-            shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */
+-        if (shy)
++        // Skip over the data descriptor. We need to correctly position the
++        // read pointer after the data descriptor for the proper detection of
++        // overlapped zip file components.
++        //
++        // We need to resolve an ambiguity over four possible data descriptor
++        // formats. We check for all four, and pick the longest match. The data
++        // descriptor can have a signature or not, and it can use four or
++        // eight-byte lengths. The zip format requires resolving the ambiguity
++        // of a signature or not, but it uses the zip64 flag to determine
++        // whether the lengths are four or eight bytes. However there is a bug
++        // in the Java zip library that applies the wrong value of that flag.
++        // This works around that bug by always trying both length formats.
++        //
++        // So why the longest match? And does this resolve the ambiguity? No,
++        // it doesn't definitively resolve the ambiguity. However choosing the
++        // longest match at least resolves it for a normal zip file, where the
++        // bytes following the data descriptor must be another zip signature
++        // that is not a data descriptor signature. There are a few specific
++        // cases for which more than one of the formats will match the given
++        // CRC and lengths. The most plausible is between four and eight-byte
++        // lengths, either with or without a signature. That only occurs for an
++        // entry with an uncompressed size of zero. We consider the data
++        // descriptor to be a vector of four-byte values. Then the possible
++        // data descriptors are [(s) 0 c 0] and [(s) 0 c 0 0 0], where (s) is
++        // the optional signature, and c is the compressed length. c would be
++        // two for the Deflate compressed data format. These look the same, so
++        // if the file contains [(s) 0 c 0 0 0], then we cannot discriminate
++        // them. However if the data descriptor was intended to be [(s) 0 c 0],
++        // then it has been followed by eight zero bytes in the zip file for
++        // some reason. For a normal zip file this cannot be the case. The data
++        // descriptor would always be immediately followed by another zip file
++        // signature, which is four bytes that are not zeros. The other cases
++        // where more than one format matches are vanishingly unlikely, but the
++        // longest match strategy resolves those as well in a normal zip file.
++        // Those pairs are [s s s] vs. [s s s s], [s s s] vs. [s s s 0 s 0],
++        // and [s s s s s] vs. [s s s s s s]. For all, s is the signature for a
++        // data descriptor. For the first two we have an entry whose CRC,
++        // compressed length, and uncompressed length are all equal (!), and
++        // are all equal to the signature (!!). If this occurs, clearly someone
++        // is messing with us. However the strategy works nonetheless. We see
++        // that if the shorter descriptor, [s s s] were what was intended, then
++        // it has been followed by either four zero bytes or a data descriptor
++        // signature. Neither can occur for a normal zip file, where it must be
++        // followed by a signature that is not a data descriptor signature. So
++        // the longest match is the correct choice. The final case is outright
++        // insane, since the compressed and uncompressed lengths are the data
++        // descriptor signature repeated twice to make a 64-bit length, which
++        // is about 6e17. The largest drive available as I write this is 100TB,
++        // which is one six thousandth of that length. If I apply Moore's law
++        // to drive capacity, we might get to 6e17 about 25 years from now. If
++        // this code is still in use then (I've seen other code I've written in
++        // use for over 30 years), then we're still in luck. A data descriptor
++        // cannot be followed by a data descriptor signature in a normal zip
++        // file. The longest match strategy continues to work.
++        //
++        // So what is a not normal zip file, where these assumptions might fall
++        // apart? zip files have been used in a non-standard way as a poor
++        // substitute for a file system, with entries deleted and perhaps
++        // others replacing them partially, with fragmented zip files being the
++        // result. Then all bets are off as to what might or might not follow a
++        // data descriptor. Though if this sort of data descriptor ambiguity
++        // falls in one of those gaps, then there should be no adverse
++        // consequences for picking the unintended one.
++        int len = 0;
++#       define SIG 0x08074b50           // optional data descriptor signature
++#ifdef LARGE_FILE_SUPPORT
++        uch buf[24];
++        int got = readbuf((char *)buf, sizeof(buf));
++        if (got >= 24 && makelong(buf) == SIG &&
++                         makelong(buf + 4) == G.lrec.crc32 &&
++                         makeint64(buf + 8) == G.lrec.csize &&
++                         makeint64(buf + 16) == G.lrec.ucsize)
++            // Have a data descriptor with a signature and 64-bit lengths.
++            len = 24;
++        else if (got >= 20 && makelong(buf) == G.lrec.crc32 &&
++                              makeint64(buf + 4) == G.lrec.csize &&
++                              makeint64(buf + 12) == G.lrec.ucsize)
++            // Have a data descriptor with no signature and 64-bit lengths.
++            len = 20;
++        else if ((G.lrec.csize >> 32) == 0 && (G.lrec.ucsize >> 32) == 0)
++            // Both lengths are short enough to fit in 32 bits.
++#else
++        uch buf[16];
++        int got = readbuf((char *)buf, sizeof(buf));
++#endif
++        {
++            if (got >= 16 && makelong(buf) == SIG &&
++                             makelong(buf + 4) == G.lrec.crc32 &&
++                             makelong(buf + 8) == G.lrec.csize &&
++                             makelong(buf + 12) == G.lrec.ucsize)
++                // Have a data descriptor with a signature and 32-bit lengths.
++                len = 16;
++            else if (got >= 12 && makelong(buf) == G.lrec.crc32 &&
++                                  makelong(buf + 4) == G.lrec.csize &&
++                                  makelong(buf + 8) == G.lrec.ucsize)
++                // Have a data descriptor with no signature and 32-bit lengths.
++                len = 12;
++        }
++        if (len == 0)
++            // There is no data descriptor that matches the entry CRC and
++            // length values.
+             error = PK_ERR;
++
++        // Back up got-len bytes, to position the read pointer after the data
++        // descriptor. Or to where the data descriptor was supposed to be, in
++        // the event none was found.
++        int back = got - len;
++        if (G.incnt + back > INBUFSIZ) {
++            // Need to load the preceding buffer. We've been here before.
++            G.cur_zipfile_bufstart -= INBUFSIZ;
++#ifdef USE_STRM_INPUT
++            zfseeko(G.zipfd, G.cur_zipfile_bufstart, SEEK_SET);
++#else /* !USE_STRM_INPUT */
++            zlseek(G.zipfd, G.cur_zipfile_bufstart, SEEK_SET);
++#endif /* ?USE_STRM_INPUT */
++            read(G.zipfd, (char *)G.inbuf, INBUFSIZ);
++            G.incnt -= INBUFSIZ - back;
++            G.inptr += INBUFSIZ - back;
++        }
++        else {
++            // Back up within current buffer.
++            G.incnt += back;
++            G.inptr -= back;
++        }
+     }
+ 
+     return error;
diff --git a/unzip-zipbomb-switch.patch b/unzip-zipbomb-switch.patch
index c6d33c0..e355afd 100644
--- a/unzip-zipbomb-switch.patch
+++ b/unzip-zipbomb-switch.patch
@@ -137,26 +137,13 @@ index 878817d..3e58071 100644
 -    if ((G.lrec.general_purpose_bit_flag & 8) != 0) {
 +    if (uO.zipbomb == TRUE) {
 +      if ((G.lrec.general_purpose_bit_flag & 8) != 0) {
-         /* skip over data descriptor (harder than it sounds, due to signature
-          * ambiguity)
-          */
-@@ -2189,16 +2196,16 @@ static int extract_or_test_member(__G)    /* return PK-type error code */
-               ((G.lrec.csize & LOW) != SIG ||   /* if not SIG, have signature */
-                (ulen == SIG &&                  /* if not SIG, no signature */
-                 (G.pInfo->zip64 ? G.lrec.csize >> 32 : G.lrec.ucsize) != SIG
--                                                /* if not SIG, have signature */
-+                /* if not SIG, have signature */
-                 )))))
--                   /* skip four more bytes to account for signature */
--                   shy += 4 - readbuf((char *)buf, 4);
-+          /* skip four more bytes to account for signature */
-+          shy += 4 - readbuf((char *)buf, 4);
-         if (G.pInfo->zip64)
--            shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */
-+          shy += 8 - readbuf((char *)buf, 8); /* skip eight more for ZIP64 */
-         if (shy)
--            error = PK_ERR;
-+          error = PK_ERR;
+         // Skip over the data descriptor. We need to correctly position the
+         // read pointer after the data descriptor for the proper detection of
+         // overlapped zip file components.
+@@ -2189,8 +2196,8 @@ static int extract_or_test_member(__G)    /* return PK-type error code */
+             G.incnt += back;
+             G.inptr -= back;
+         }
 +      }
      }
 -
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/unzip.git/commitdiff/6a7f58c9608e77b3db1a6a5b5f1861f924ac14c2



More information about the pld-cvs-commit mailing list