zip23-CAN-2004-1010-patch.txt   [plain text]


This patch, based on why by Josh Bressers of Red Hat, fixes CAN-2004-1010,
a buffer overflow bug in Zip 2.3 involving recursive traversal of very deep
paths.  (The bug could affect systems that use Zip to perform automated
backups of untrusted user data, for example.)  The Unix version of this
patch has been tested on Linux and appears to work correctly.  Other ports
either have not been tested or have been tested only lightly, but insofar
as the fixes are nearly identical across all platforms, this should not be
considered particularly worrisome.

To apply:

	- cd into the Zip 2.3 source directory
	- type something similar to this:

		patch -p1 < filename_of_this_patch

	- read the Zip 2.3 INSTALL file and compile as usual

Please report any problems or questions to

	http://www.info-zip.org/zip-bug.html

Test procedure on Linux (255-character directory names are the maximum
allowed):

	mkdir /tmp/longdirs
	cd /tmp/longdirs
	mkdir `perl -e 'print "a" x 255'`
	cd `perl -e 'print "a" x 255'`
	mkdir `perl -e 'print "b" x 255'`
	cd `perl -e 'print "b" x 255'`
	mkdir `perl -e 'print "c" x 255'`
	cd `perl -e 'print "c" x 255'`
	mkdir `perl -e 'print "d" x 255'`
	cd `perl -e 'print "d" x 255'`
	mkdir `perl -e 'print "e" x 255'`
	cd `perl -e 'print "e" x 255'`
	mkdir `perl -e 'print "f" x 255'`
	cd `perl -e 'print "f" x 255'`
	mkdir `perl -e 'print "g" x 255'`
	cd `perl -e 'print "g" x 255'`
	mkdir `perl -e 'print "h" x 255'`
	cd `perl -e 'print "h" x 255'`
	mkdir `perl -e 'print "i" x 255'`
	cd /tmp/longdirs
	tree -d
	zip-2.3-stock -r test-CAN-2004-1010.zip `perl -e 'print "a" x 255'`
		[segfaults]
	zip-2.3-fixed -r test-CAN-2004-1010.zip `perl -e 'print "a" x 255'`
		[succeeds]

References:

	http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-1010
	http://seclists.org/lists/fulldisclosure/2004/Nov/0150.html
	http://secunia.com/advisories/13094/
	https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=138228

G. Roelofs (temporarily acting as Zip 2.3 maintainer)
20041114, 20041209


diff -ru zip-2.3-stock/acorn/acornzip.c zip-2.3/acorn/acornzip.c
--- zip-2.3-stock/acorn/acornzip.c	1999-11-07 02:32:30.000000000 -0800
+++ zip-2.3/acorn/acornzip.c	2004-11-14 15:42:32.000000000 -0800
@@ -419,8 +419,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -431,6 +431,11 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '.')
     name[len - 1] = '\0';
@@ -441,11 +446,13 @@
     s.st_mode = (S_IREAD|S_IWRITE|S_IFREG);
     s.st_size = -1;
     s.st_mtime = time(&s.st_mtime);
-  } else if (LSSTAT(name, &s) != 0)
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -461,6 +468,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime((time_t *) &s.st_mtime);
 }
 
diff -ru zip-2.3-stock/amiga/amigazip.c zip-2.3/amiga/amigazip.c
--- zip-2.3-stock/amiga/amigazip.c	1999-11-07 02:34:30.000000000 -0800
+++ zip-2.3/amiga/amigazip.c	2004-11-14 16:19:44.000000000 -0800
@@ -320,8 +320,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -332,19 +332,28 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
 
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (SSTAT(name, &s) != 0)
+    }
+  } else if (SSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -360,6 +369,8 @@
     t->ctime = s.st_ctime;
   }
 
+   free(name);
+
    return unix2dostime(&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/aosvs/aosvs.c zip-2.3/aosvs/aosvs.c
--- zip-2.3-stock/aosvs/aosvs.c	1999-11-07 02:36:16.000000000 -0800
+++ zip-2.3/aosvs/aosvs.c	2004-11-14 16:20:16.000000000 -0800
@@ -294,8 +294,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -306,19 +306,28 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
 
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -334,6 +343,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime(&s.st_ctime);
 }
 
diff -ru zip-2.3-stock/atari/atari.c zip-2.3/atari/atari.c
--- zip-2.3-stock/atari/atari.c	1999-11-07 02:36:37.000000000 -0800
+++ zip-2.3/atari/atari.c	2004-11-14 16:20:27.000000000 -0800
@@ -532,8 +532,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -544,19 +544,28 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
 
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
 /*  *a = ((ulg)s.st_mode << 16) | (ulg)GetFileMode(name); */
@@ -570,6 +579,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime(&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/beos/beos.c zip-2.3/beos/beos.c
--- zip-2.3-stock/beos/beos.c	1999-11-29 11:31:41.000000000 -0800
+++ zip-2.3/beos/beos.c	2004-11-14 16:20:38.000000000 -0800
@@ -283,8 +283,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -295,18 +295,27 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -322,6 +331,8 @@
     t->ctime = s.st_mtime;   /* best guess (s.st_ctime: last status change!) */
   }
 
+  free(name);
+
   return unix2dostime(&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/human68k/human68k.c zip-2.3/human68k/human68k.c
--- zip-2.3-stock/human68k/human68k.c	1999-11-07 02:39:07.000000000 -0800
+++ zip-2.3/human68k/human68k.c	2004-11-14 16:21:00.000000000 -0800
@@ -231,8 +231,13 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f), isstdin = !strcmp(f, "-");
+  char *name;
+  unsigned int len = strlen(f);
+  int isstdin = !strcmp(f, "-");
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
 
   strcpy(name, f);
   if (name[len - 1] == '/')
@@ -240,13 +245,17 @@
   /* not all systems allow stat'ing a file with / appended */
 
   if (isstdin) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     int atr = _dos_chmod(name, -1);
@@ -263,6 +272,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime(&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/macos/source/macos.c zip-2.3/macos/source/macos.c
--- zip-2.3-stock/macos/source/macos.c	1999-11-07 02:41:30.000000000 -0800
+++ zip-2.3/macos/source/macos.c	2004-11-14 16:37:07.000000000 -0800
@@ -601,17 +601,12 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[NAME_MAX];
-  int len;
 
   AssertStr(f,f)
 
-  len = strlen(f);
-  sstrcpy(name, f);
+  if (strlen(f) == 0) return 0;
 
-  if (len == 0) return 0;
-
-  if (SSTAT(name, &s) != 0)
+  if (SSTAT(f, &s) != 0)
              /* Accept about any file kind including directories
               * (stored with trailing : with -r option)
               */
diff -ru zip-2.3-stock/msdos/msdos.c zip-2.3/msdos/msdos.c
--- zip-2.3-stock/msdos/msdos.c	1999-11-07 02:45:05.000000000 -0800
+++ zip-2.3/msdos/msdos.c	2004-11-14 16:21:12.000000000 -0800
@@ -552,8 +552,9 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f), isstdin = !strcmp(f, "-");
+  char *name;
+  unsigned int len = strlen(f);
+  int isstdin = !strcmp(f, "-");
 
   if (f == label) {
     if (a != NULL)
@@ -564,20 +565,29 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
 
   if (isstdin) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
+    }
     time((time_t *)&s.st_mtime);       /* some fstat()s return time zero */
-  } else if (LSSTAT(name, &s) != 0)
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL)
     *a = ((ulg)s.st_mode << 16) | (isstdin ? 0L : (ulg)GetFileMode(name));
@@ -589,6 +599,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime((time_t *)&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/os2/os2.c zip-2.3/os2/os2.c
--- zip-2.3-stock/os2/os2.c	1999-11-07 02:45:55.000000000 -0800
+++ zip-2.3/os2/os2.c	2004-11-14 16:15:02.000000000 -0800
@@ -387,8 +387,9 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f), isstdin = !strcmp(f, "-");
+  char *name;
+  unsigned int len = strlen(f);
+  int isstdin = !strcmp(f, "-");
 
   if (f == label) {
     if (a != NULL)
@@ -399,6 +400,11 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
@@ -412,11 +418,13 @@
     }
     time(&s.st_ctime);
     s.st_atime = s.st_mtime = s.st_ctime;
-  } else if (LSSTAT(name, &s) != 0)
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | (isstdin ? 0L : (ulg)GetFileMode(name));
@@ -436,6 +444,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return GetFileTime(name);
 }
 
diff -ru zip-2.3-stock/qdos/qfileio.c zip-2.3/qdos/qfileio.c
--- zip-2.3-stock/qdos/qfileio.c	1999-11-07 13:11:41.000000000 -0800
+++ zip-2.3/qdos/qfileio.c	2004-11-14 16:22:03.000000000 -0800
@@ -162,8 +162,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -174,18 +174,27 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -201,6 +210,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime(&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/tandem/tanzip.c zip-2.3/tandem/tanzip.c
--- zip-2.3-stock/tandem/tanzip.c	1999-11-07 02:49:09.000000000 -0800
+++ zip-2.3/tandem/tanzip.c	2004-11-14 16:05:08.000000000 -0800
@@ -491,7 +491,6 @@
     iztimes *t;             /* return value: access and modification time */
   {
     struct stat s;
-    char fname[FILENAME_MAX + 1];
     nsk_stat_ov *nsk_ov;
 
     if (strcmp(f, "-") == 0) {    /* if compressing stdin */
@@ -500,9 +499,7 @@
       }
     }
 
-    strcpy(fname, f);
-
-    if (stat(fname, &s) != 0) return 0;
+    if (stat(f, &s) != 0) return 0;
 
     if (a!= NULL) {
       *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWUSR);
diff -ru zip-2.3-stock/theos/theos.c zip-2.3/theos/theos.c
--- zip-2.3-stock/theos/theos.c	1999-11-07 02:50:43.000000000 -0800
+++ zip-2.3/theos/theos.c	2004-11-14 16:22:36.000000000 -0800
@@ -409,8 +409,8 @@
    a device, return a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -421,6 +421,11 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
 
   if (name[len - 1] == '/' || name[len - 1] == '.')
@@ -428,13 +433,17 @@
 
   /* not all systems allow stat'ing a file with / appended */
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
     /* Accept about any file kind including directories
      * (stored with trailing / with -r option)
      */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -442,8 +451,8 @@
      || (s.st_mode & S_IFMT) == S_IFLIB) {
       *a |= MSDOS_DIR_ATTR;
     }
-/* Map Theos' hidden attribute to DOS's hidden attribute */
-    if (!st.st_protect & 0x80))
+    /* Map Theos' hidden attribute to DOS's hidden attribute */
+    if (!(st.st_protect & 0x80))
       *a |= MSDOS_HIDDEN_ATTR;
     *a |= ((ulg) s.st_protect) << 8;
   }
@@ -454,6 +463,9 @@
     t->mtime = s.st_mtime;
     t->ctime = t->mtime;   /* best guess, (s.st_ctime: last status change!!) */
   }
+
+  free(name);
+
   return unix2dostime(&s.st_mtime);
 }
 /*
diff -ru zip-2.3-stock/tops20/tops20.c zip-2.3/tops20/tops20.c
--- zip-2.3-stock/tops20/tops20.c	1999-11-07 02:51:11.000000000 -0800
+++ zip-2.3/tops20/tops20.c	2004-11-14 16:24:08.000000000 -0800
@@ -422,8 +422,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -434,19 +434,28 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
 
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -462,6 +471,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime(&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/unix/unix.c zip-2.3/unix/unix.c
--- zip-2.3-stock/unix/unix.c	1999-11-07 06:08:21.000000000 -0800
+++ zip-2.3/unix/unix.c	2004-11-14 16:26:29.000000000 -0800
@@ -319,8 +319,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -331,19 +331,28 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
+    }
   }
-  else if (LSSTAT(name, &s) != 0)
+  else if (LSSTAT(name, &s) != 0) {
     /* Accept about any file kind including directories
      * (stored with trailing / with -r option)
      */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
 #ifndef OS390
@@ -367,7 +376,7 @@
     {
     mode_t legacy_modes;
 
-    /* Initialize with permission bits - which are not implementation optional */
+    /* Initialize with permission bits--which are not implementation-optional */
     legacy_modes = s.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
     if (S_ISDIR(s.st_mode))
       legacy_modes |= UNX_IFDIR;
@@ -397,6 +406,9 @@
     t->mtime = s.st_mtime;
     t->ctime = t->mtime;   /* best guess, (s.st_ctime: last status change!!) */
   }
+
+  free(name);
+
   return unix2dostime(&s.st_mtime);
 }
 
diff -ru zip-2.3-stock/vms/vmszip.c zip-2.3/vms/vmszip.c
--- zip-2.3-stock/vms/vmszip.c	1999-11-07 02:53:45.000000000 -0800
+++ zip-2.3/vms/vmszip.c	2004-11-14 16:24:18.000000000 -0800
@@ -488,8 +488,8 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f);
+  char *name;
+  unsigned int len = strlen(f);
 
   if (f == label) {
     if (a != NULL)
@@ -500,19 +500,28 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (name[len - 1] == '/')
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
 
   if (strcmp(f, "-") == 0) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
-  } else if (LSSTAT(name, &s) != 0)
+    }
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | !(s.st_mode & S_IWRITE);
@@ -532,6 +541,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
 #ifdef USE_MTIME
   return unix2dostime((time_t *)&s.st_mtime); /* Use modification time in VMS */
 #else
diff -ru zip-2.3-stock/win32/win32zip.c zip-2.3/win32/win32zip.c
--- zip-2.3-stock/win32/win32zip.c	1999-11-08 11:59:16.000000000 -0800
+++ zip-2.3/win32/win32zip.c	2004-11-14 16:24:33.000000000 -0800
@@ -522,8 +522,9 @@
    a file size of -1 */
 {
   struct stat s;        /* results of stat() */
-  char name[FNMAX];
-  int len = strlen(f), isstdin = !strcmp(f, "-");
+  char *name;
+  unsigned int len = strlen(f);
+  int isstdin = !strcmp(f, "-");
 
   if (f == label) {
     if (a != NULL)
@@ -534,20 +535,29 @@
       t->atime = t->mtime = t->ctime = label_utim;
     return label_time;
   }
+
+  name = malloc(len+1);
+  if (!name)
+    return 0;  /* ideally, would like to report alloc-failure warning/error */
+
   strcpy(name, f);
   if (MBSRCHR(name, '/') == (name + len - 1))
     name[len - 1] = '\0';
   /* not all systems allow stat'ing a file with / appended */
 
   if (isstdin) {
-    if (fstat(fileno(stdin), &s) != 0)
+    if (fstat(fileno(stdin), &s) != 0) {
+      free(name);
       error("fstat(stdin)");
+    }
     time((time_t *)&s.st_mtime);       /* some fstat()s return time zero */
-  } else if (LSSTAT(name, &s) != 0)
+  } else if (LSSTAT(name, &s) != 0) {
              /* Accept about any file kind including directories
               * (stored with trailing / with -r option)
               */
+    free(name);
     return 0;
+  }
 
   if (a != NULL) {
     *a = ((ulg)s.st_mode << 16) | (isstdin ? 0L : (ulg)GetFileMode(name));
@@ -560,6 +570,8 @@
     t->ctime = s.st_ctime;
   }
 
+  free(name);
+
   return unix2dostime((time_t *)&s.st_mtime);
 }