Subject: [INSTALLED PATCH] gzip: fix some Y2038 etc. bugs

From: Paul Eggert <[email-masked]>
Date: Tue, 6 Sep 2016 14:53: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              |  6 ++++++
 gzip.c            | 11 +++++++++--
 tests/Makefile.am |  1 +
 tests/timestamp   | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 zip.c             | 12 +++++++++---
 5 files changed, 77 insertions(+), 5 deletions(-)
 create mode 100755 tests/timestamp

diff --git a/NEWS b/NEWS
index 8c81f5c..6532550 100644
--- 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 0d0953c..0fca5a3 100644
--- 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.  */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 71cf4ad..1c24886 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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
index 0000000..c488760
--- /dev/null
+++ b/tests/timestamp
@@ -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 793cf42..eb60409 100644
--- 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 */
-- 
2.7.4