gzip: fix some Y2038 etc. bugs
authorPaul Eggert <eggert@cs.ucla.edu>
Tue, 6 Sep 2016 21:51:47 +0000 (14:51 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Tue, 6 Sep 2016 21:53:32 +0000 (14:53 -0700)
* NEWS: Document this.
* gzip.c (get_method): Warn about out-of-range MTIME,
and ignore it instead of relying on possibly-undefined behavior.
* tests/Makefile.am (TESTS): Add timestamp.
* tests/timestamp: New test.
* zip.c (zip): Warn about out-of-range file time stamp.

NEWS
gzip.c
tests/Makefile.am
tests/timestamp [new file with mode: 0755]
zip.c

diff --git a/NEWS b/NEWS
index 8c81f5c901887870c1efbe90b774040714ece770..6532550b11710e20ed852b36f75419012b370048 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,12 @@ GNU gzip NEWS                                    -*- outline -*-
 
 ** Bug fixes
 
+  gzip now warns about file time stamps out of gzip range, or out of
+  time_t range, instead of silently continuing, sometimes with
+  undefined behavior.  This affects time stamps before 1970 or after
+  2106, and time stamps after 2038 on 32-bit platforms.
+  [bug present since the beginning]
+
   Support for VMS and Amiga has been removed.  It was not working anyway,
   and it reportedly caused file name glitches on MS-Windowsish platforms.
 
diff --git a/gzip.c b/gzip.c
index 0d0953cda2e2cb4b642dd5191faff2b63f8189e7..0fca5a3b129851e7077ae05457e42e8ebc85fcc6 100644 (file)
--- a/gzip.c
+++ b/gzip.c
@@ -1537,8 +1537,15 @@ local int get_method(in)
         stamp |= ((ulg)get_byte()) << 24;
         if (stamp != 0 && !no_time)
           {
-            time_stamp.tv_sec = stamp;
-            time_stamp.tv_nsec = 0;
+            if (stamp <= TYPE_MAXIMUM (time_t))
+              {
+                time_stamp.tv_sec = stamp;
+                time_stamp.tv_nsec = 0;
+              }
+            else
+              WARN ((stderr,
+                     "%s: %s: MTIME %lu out of range for this platform\n",
+                     program_name, ifname, stamp));
           }
 
         magic[8] = get_byte ();  /* Ignore extra flags.  */
index 71cf4ada6bc22625c2a12e289a52f58503918774..1c248864aaf3eed79e9c264e9c93e24fecc57762 100644 (file)
@@ -25,6 +25,7 @@ TESTS =                                       \
   mixed                                        \
   null-suffix-clobber                  \
   stdin                                        \
+  timestamp                            \
   trailing-nul                         \
   unpack-invalid                       \
   z-suffix                             \
diff --git a/tests/timestamp b/tests/timestamp
new file mode 100755 (executable)
index 0000000..c488760
--- /dev/null
@@ -0,0 +1,52 @@
+#!/bin/sh
+# Exercise timestamps.
+
+# Copyright 2016 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# limit so don't run it by default.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ..
+
+TZ=UTC0
+export TZ
+
+# On platforms supporting time stamps outside gzip's range,
+# test that gzip warns when converting them to gzip format.
+for time in 190101010000 196912312359.59 197001010000 210602070628.16; do
+  if touch -t $time in; then
+     gzip in
+     test $? = 2 || fail=1
+  fi
+  rm -f in in.gz
+done
+
+# Test that time stamps in range for gzip do not generate warnings.
+for time in 197001010000.01 203801190314.07 203801190314.08 210602070628.15; do
+  if touch -t $time in; then
+     gzip in || fail=1
+  fi
+  rm -f in in.gz
+done
+
+# On platforms that fail to support time stamps within gzip's range,
+# test that gzip warns when converting them from gzip format.
+touch -t 210602070628.15 in || {
+  printf '\037\213\10\0\377\377\377\377\0\377\3\0\0\0\0\0\0\0\0\0' >y2106.gz ||
+    framework_failure_
+  gzip -Nlv <y2106.gz
+  test $? = 2 || fail=1
+}
+
+Exit $fail
diff --git a/zip.c b/zip.c
index 793cf4257f33a4176d64caad846591de70a3a225..eb60409c7edc1851f31671921ba9d09806c1bb6f 100644 (file)
--- a/zip.c
+++ b/zip.c
@@ -54,9 +54,15 @@ int zip(in, out)
         flags |= ORIG_NAME;
     }
     put_byte(flags);         /* general flags */
-    stamp = (0 <= time_stamp.tv_sec && time_stamp.tv_sec <= 0xffffffff
-             ? (ulg) time_stamp.tv_sec
-             : (ulg) 0);
+    if (0 < time_stamp.tv_sec && time_stamp.tv_sec <= 0xffffffff)
+      stamp = time_stamp.tv_sec;
+    else
+      {
+        /* It's intended that time stamp 0 generates this warning,
+           since gzip format reserves 0 for something else.  */
+        warning ("file time stamp out of range for gzip format");
+        stamp = 0;
+      }
     put_long (stamp);
 
     /* Write deflated file to zip file */