>From 5b2fe53044217558da419a6ca384a61011ae293e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 7 Nov 2017 21:01:13 -0800 Subject: [PATCH] gzip: diagnose out-of-range MTIME This seems to be the problem reported by Bruno Haible for GNU/Hurd i386 with touch 8.26 and glibc 2.24 (Bug#29033#20). * NEWS: Document this. * gzip.c (get_method): If MTIME is out of range for this platform, warn and substitute the nearest in-range value, instead of silently ignoring it. (do_list): Remove no-longer-needed test for unknown time stamp. --- NEWS | 12 ++++++++---- gzip.c | 31 +++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index 2b7a168..34eae0d 100644 --- a/NEWS +++ b/NEWS @@ -13,10 +13,14 @@ GNU gzip NEWS -*- outline -*- When decompressing data in 'pack' format, gzip no longer mishandles leading zeros in the end-of-block code. [bug introduced in gzip-1.6] - When converting timestamps to gzip file format (32-bit unsigned) or - to time_t format (system-dependent), gzip now ignores out-of-range - values instead of shoehorning them into the destination format, - sometimes with undefined behavior. This affects timestamps before + When converting from system-dependent time_t format to the 32-bit + unsigned MTIME format used in gzip files, if a timestamp does not + fit gzip now substitutes zero instead of the timestamp's low-order + 32 bits, as per Internet RFC 1952. When converting from MTIME to + time_t format, if a timestamp does not fit gzip now warns and + substitutes the nearest in-range value instead of crashing or + silently substituting an implementation-defined value (typically, + the timestamp's low-order bits). This affects timestamps before 1970 and after 2106, and timestamps after 2038 on platforms with 32-bit signed time_t. [bug present since the beginning] diff --git a/gzip.c b/gzip.c index cfc4fe3..637b791 100644 --- a/gzip.c +++ b/gzip.c @@ -195,10 +195,12 @@ static char *env; /* contents of GZIP env variable */ static char const *z_suffix; /* default suffix (can be set with --suffix) */ static size_t z_len; /* strlen(z_suffix) */ -/* The original timestamp (modification time). Its tv_nsec component - is negative if the original time is unknown or is out of time_t - range; the latter can happen on hosts with 32-bit signed time_t - because the gzip format's MTIME is 32-bit unsigned. */ +/* The original timestamp (modification time). If the original is + unknown, TIME_STAMP.tv_nsec is negative. If the original is + greater than struct timespec range, TIME_STAMP is the maximal + struct timespec value; this can happen on hosts with 32-bit signed + time_t because the gzip format's MTIME is 32-bit unsigned. + The original cannot be less than struct timespec range. */ struct timespec time_stamp; /* The set of signals that are caught. */ @@ -1546,10 +1548,21 @@ local int get_method(in) stamp |= ((ulg)get_byte()) << 8; stamp |= ((ulg)get_byte()) << 16; stamp |= ((ulg)get_byte()) << 24; - if (!no_time && 0 < stamp && stamp <= TYPE_MAXIMUM (time_t)) + 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)); + time_stamp.tv_sec = TYPE_MAXIMUM (time_t); + time_stamp.tv_nsec = TIMESPEC_RESOLUTION - 1; + } } magic[8] = get_byte (); /* Ignore extra flags. */ @@ -1778,9 +1791,7 @@ local void do_list(ifd, method) static char const month_abbr[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - struct tm *tm = (time_stamp.tv_nsec < 0 - ? NULL - : localtime (&time_stamp.tv_sec)); + struct tm *tm = localtime (&time_stamp.tv_sec); printf ("%5s %08lx ", methods[method], crc); if (tm) printf ("%s%3d %02d:%02d ", month_abbr[tm->tm_mon], -- 2.13.6