From patchwork Tue Jun 19 21:01:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Albert ARIBAUD X-Patchwork-Id: 27930 Received: (qmail 13397 invoked by alias); 19 Jun 2018 21:01:23 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 13235 invoked by uid 89); 19 Jun 2018 21:01:22 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-26.6 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.2 spammy=mid, SHR, complement, bs X-HELO: smtp1-g21.free.fr From: "Albert ARIBAUD (3ADEV)" To: libc-alpha@sourceware.org Cc: "Albert ARIBAUD (3ADEV)" Subject: [PATCH v2 1/1] Y2038: make __mktime_internal compatible with 64-bit-time Date: Tue, 19 Jun 2018 23:01:12 +0200 Message-Id: <20180619210112.1600-2-albert.aribaud@3adev.fr> In-Reply-To: <20180619210112.1600-1-albert.aribaud@3adev.fr> References: <20180619210112.1600-1-albert.aribaud@3adev.fr> This implies that its callers be 64-bit-time compatible too. It is done by creating 64-bit-time versions of these and turning their original 32-bit-time versions into wrappers (at a slight execution time cost). The callers affected are: * mktime * timelocal (as an alias of mktime) * timegm --- include/time.h | 12 ++-- time/mktime.c | 180 ++++++++++++++++++++++++------------------------- time/timegm.c | 24 +++++-- 3 files changed, 112 insertions(+), 104 deletions(-) diff --git a/include/time.h b/include/time.h index eaec898fac..8c261abcf4 100644 --- a/include/time.h +++ b/include/time.h @@ -50,13 +50,13 @@ extern void __tzset_parse_tz (const char *tz) attribute_hidden; extern void __tz_compute (__time64_t timer, struct tm *tm, int use_localtime) __THROW attribute_hidden; -/* Subroutine of `mktime'. Return the `time_t' representation of TP and - normalize TP, given that a `struct tm *' maps to a `time_t' as performed +/* Subroutine of `mktime'. Return the `__time64_t' representation of TP and + normalize TP, given that a `struct tm *' maps to a `__time64_t' as performed by FUNC. Keep track of next guess for time_t offset in *OFFSET. */ -extern time_t __mktime_internal (struct tm *__tp, - struct tm *(*__func) (const time_t *, - struct tm *), - time_t *__offset) attribute_hidden; +extern __time64_t __mktime_internal (struct tm *__tp, + struct tm *(*__func) (const __time64_t *, + struct tm *), + __time64_t *__offset) attribute_hidden; /* nis/nis_print.c needs ctime, so even if ctime is not declared here, we define __ctime64 as ctime so that nis/nis_print.c can get linked diff --git a/time/mktime.c b/time/mktime.c index 5f038a212f..18837c87cd 100644 --- a/time/mktime.c +++ b/time/mktime.c @@ -1,4 +1,4 @@ -/* Convert a 'struct tm' to a time_t value. +/* Convert a 'struct tm' to a __time64_t value. Copyright (C) 1993-2018 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Paul Eggert . @@ -35,6 +35,7 @@ #include #include +#include #include /* For the real memcpy prototype. */ @@ -92,50 +93,36 @@ verify (long_int_is_wide_enough, INT_MAX == INT_MAX * (long_int) 2 / 2); #define SHR(a, b) \ ((-1 >> 1 == -1 \ && (long_int) -1 >> 1 == -1 \ - && ((time_t) -1 >> 1 == -1 || ! TYPE_SIGNED (time_t))) \ + && (__time64_t) -1 >> 1 == -1) \ ? (a) >> (b) \ : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0)) /* The extra casts in the following macros work around compiler bugs, e.g., in Cray C 5.0.3.0. */ -/* True if the arithmetic type T is an integer type. bool counts as - an integer. */ -#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1) - /* True if negative values of the signed integer type T use two's complement, or if T is an unsigned integer type. */ #define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1) -/* True if the arithmetic type T is signed. */ -#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) - /* The maximum and minimum values for the integer type T. These macros have undefined behavior if T is signed and has padding bits. If this is a problem for you, please let us know how to fix it for your host. */ -#define TYPE_MINIMUM(t) \ - ((t) (! TYPE_SIGNED (t) \ - ? (t) 0 \ - : ~ TYPE_MAXIMUM (t))) -#define TYPE_MAXIMUM(t) \ - ((t) (! TYPE_SIGNED (t) \ - ? (t) -1 \ - : ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1))) - -#ifndef TIME_T_MIN -# define TIME_T_MIN TYPE_MINIMUM (time_t) +#define TYPE_MINIMUM(t) (~ TYPE_MAXIMUM (t)) +#define TYPE_MAXIMUM(t) ((((t) 1 << (sizeof (t) * CHAR_BIT - 2)) - 1) * 2 + 1) + +#ifndef TIME64_T_MIN +# define TIME64_T_MIN TYPE_MINIMUM (__time64_t) #endif -#ifndef TIME_T_MAX -# define TIME_T_MAX TYPE_MAXIMUM (time_t) +#ifndef TIME64_T_MAX +# define TIME64_T_MAX TYPE_MAXIMUM (__time64_t) #endif -#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1) +#define TIME_T_MIDPOINT (SHR (TIME64_T_MIN + TIME64_T_MAX, 1) + 1) -verify (time_t_is_integer, TYPE_IS_INTEGER (time_t)); verify (twos_complement_arithmetic, (TYPE_TWOS_COMPLEMENT (int) && TYPE_TWOS_COMPLEMENT (long_int) - && TYPE_TWOS_COMPLEMENT (time_t))); + && TYPE_TWOS_COMPLEMENT (__time64_t))); #define EPOCH_YEAR 1970 #define TM_YEAR_BASE 1900 @@ -196,7 +183,7 @@ isdst_differ (int a, int b) The result may overflow. It is the caller's responsibility to detect overflow. */ -static time_t +static __time64_t ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1, int year0, int yday0, int hour0, int min0, int sec0) { @@ -212,80 +199,73 @@ ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1, int b400 = SHR (b100, 2); int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400); - /* Compute the desired time in time_t precision. Overflow might + /* Compute the desired time in __time64_t precision. Overflow might occur here. */ - time_t tyear1 = year1; - time_t years = tyear1 - year0; - time_t days = 365 * years + yday1 - yday0 + intervening_leap_days; - time_t hours = 24 * days + hour1 - hour0; - time_t minutes = 60 * hours + min1 - min0; - time_t seconds = 60 * minutes + sec1 - sec0; + __time64_t tyear1 = year1; + __time64_t years = tyear1 - year0; + __time64_t days = 365 * years + yday1 - yday0 + intervening_leap_days; + __time64_t hours = 24 * days + hour1 - hour0; + __time64_t minutes = 60 * hours + min1 - min0; + __time64_t seconds = 60 * minutes + sec1 - sec0; return seconds; } /* Return the average of A and B, even if A + B would overflow. */ -static time_t -time_t_avg (time_t a, time_t b) +static __time64_t +time_t_avg (__time64_t a, __time64_t b) { return SHR (a, 1) + SHR (b, 1) + (a & b & 1); } -/* Return 1 if A + B does not overflow. If time_t is unsigned and if - B's top bit is set, assume that the sum represents A - -B, and - return 1 if the subtraction does not wrap around. */ +/* Return 1 if A + B does not overflow. */ static int -time_t_add_ok (time_t a, time_t b) +time_t_add_ok (__time64_t a, __time64_t b) { - if (! TYPE_SIGNED (time_t)) - { - time_t sum = a + b; - return (sum < a) == (TIME_T_MIDPOINT <= b); - } - else if (WRAPV) + if (WRAPV) { - time_t sum = a + b; + __time64_t sum = a + b; return (sum < a) == (b < 0); } else { - time_t avg = time_t_avg (a, b); - return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2; + __time64_t avg = time_t_avg (a, b); + return TIME64_T_MIN / 2 <= avg && avg <= TIME64_T_MAX / 2; } } /* Return 1 if A + B does not overflow. */ static int -time_t_int_add_ok (time_t a, int b) +time_t_int_add_ok (__time64_t a, int b) { - verify (int_no_wider_than_time_t, INT_MAX <= TIME_T_MAX); + verify (int_no_wider_than_time_t, INT_MAX <= TIME64_T_MAX); if (WRAPV) { - time_t sum = a + b; + __time64_t sum = a + b; return (sum < a) == (b < 0); } else { int a_odd = a & 1; - time_t avg = SHR (a, 1) + (SHR (b, 1) + (a_odd & b)); - return TIME_T_MIN / 2 <= avg && avg <= TIME_T_MAX / 2; + __time64_t avg = SHR (a, 1) + (SHR (b, 1) + (a_odd & b)); + return TIME64_T_MIN / 2 <= avg && avg <= TIME64_T_MAX / 2; } } -/* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC), +/* Return a __time64_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC), assuming that *T corresponds to *TP and that no clock adjustments occurred between *TP and the desired time. If TP is null, return a value not equal to *T; this avoids false matches. If overflow occurs, yield the minimal or maximal value, except do not yield a value equal to *T. */ -static time_t +static __time64_t guess_time_tm (long_int year, long_int yday, int hour, int min, int sec, - const time_t *t, const struct tm *tp) + const __time64_t *t, const struct tm *tp) { if (tp) { - time_t d = ydhms_diff (year, yday, hour, min, sec, - tp->tm_year, tp->tm_yday, - tp->tm_hour, tp->tm_min, tp->tm_sec); + __time64_t d = ydhms_diff (year, yday, hour, min, sec, + tp->tm_year, tp->tm_yday, + tp->tm_hour, tp->tm_min, tp->tm_sec); if (time_t_add_ok (*t, d)) return *t + d; } @@ -296,30 +276,30 @@ guess_time_tm (long_int year, long_int yday, int hour, int min, int sec, match; and don't oscillate between two values, as that would confuse the spring-forward gap detector. */ return (*t < TIME_T_MIDPOINT - ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN) - : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX)); + ? (*t <= TIME64_T_MIN + 1 ? *t + 1 : TIME64_T_MIN) + : (TIME64_T_MAX - 1 <= *t ? *t - 1 : TIME64_T_MAX)); } /* Use CONVERT to convert *T to a broken down time in *TP. If *T is out of range for conversion, adjust it so that it is the nearest in-range value and then convert that. */ static struct tm * -ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), - time_t *t, struct tm *tp) +ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *), + __time64_t *t, struct tm *tp) { struct tm *r = convert (t, tp); if (!r && *t) { - time_t bad = *t; - time_t ok = 0; + __time64_t bad = *t; + __time64_t ok = 0; - /* BAD is a known unconvertible time_t, and OK is a known good one. + /* BAD is a known unconvertible __time64_t, and OK is a known good one. Use binary search to narrow the range between BAD and OK until they differ by 1. */ while (bad != ok + (bad < 0 ? -1 : 1)) { - time_t mid = *t = time_t_avg (ok, bad); + __time64_t mid = *t = time_t_avg (ok, bad); r = convert (t, tp); if (r) ok = mid; @@ -340,18 +320,18 @@ ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), } -/* Convert *TP to a time_t value, inverting +/* Convert *TP to a __time64_t value, inverting the monotonic and mostly-unit-linear conversion function CONVERT. Use *OFFSET to keep track of a guess at the offset of the result, compared to what the result would be for UTC without leap seconds. If *OFFSET's guess is correct, only one CONVERT call is needed. This function is external because it is used also by timegm.c. */ -time_t +__time64_t __mktime_internal (struct tm *tp, - struct tm *(*convert) (const time_t *, struct tm *), - time_t *offset) + struct tm *(*convert) (const __time64_t *, struct tm *), + __time64_t *offset) { - time_t t, gt, t0, t1, t2; + __time64_t t, gt, t0, t1, t2; struct tm tm; /* The maximum number of probes (calls to CONVERT) should be enough @@ -382,7 +362,7 @@ __mktime_internal (struct tm *tp, /* The other values need not be in range: the remaining code handles minor overflows correctly, - assuming int and time_t arithmetic wraps around. + assuming int and __time64_t arithmetic wraps around. Major overflows are caught at the end. */ /* Calculate day of year from year, month, and day of month. @@ -393,7 +373,7 @@ __mktime_internal (struct tm *tp, long_int lmday = mday; long_int yday = mon_yday + lmday; - time_t guessed_offset = *offset; + __time64_t guessed_offset = *offset; int sec_requested = sec; @@ -413,12 +393,12 @@ __mktime_internal (struct tm *tp, t0 = ydhms_diff (year, yday, hour, min, sec, EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset); - if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) + if (TIME64_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) { - /* time_t isn't large enough to rule out overflows, so check - for major overflows. A gross check suffices, since if t0 - has overflowed, it is off by a multiple of TIME_T_MAX - - TIME_T_MIN + 1. So ignore any component of the difference + /* __time64_t is large enough to rule out overflows, but check + for major overflows anyway. A gross check suffices, since if t0 + has overflowed, it is off by a multiple of TIME64_T_MAX - + TIME64_T_MIN + 1. So ignore any component of the difference that is bounded by a small value. */ /* Approximate log base 2 of the number of time units per @@ -447,20 +427,21 @@ __mktime_internal (struct tm *tp, int diff = approx_biennia - approx_requested_biennia; int approx_abs_diff = diff < 0 ? -1 - diff : diff; - /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously + /* IRIX 4.0.5 cc miscalculates 32-bit TIME_T_MIN / 3: it erroneously gives a positive value of 715827882. Setting a variable - first then doing math on it seems to work. - (ghazi@caip.rutgers.edu) */ - time_t time_t_max = TIME_T_MAX; - time_t time_t_min = TIME_T_MIN; - time_t overflow_threshold = + first then doing math on it seems to work (ghazi@caip.rutgers.edu). + Do this with 64-bit TIME64_T_MIN too just in case. + */ + __time64_t time_t_max = TIME64_T_MAX; + __time64_t time_t_min = TIME64_T_MIN; + __time64_t overflow_threshold = (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM; if (overflow_threshold < approx_abs_diff) { /* Overflow occurred. Try repairing it; this might work if the time zone offset is enough to undo the overflow. */ - time_t repaired_t0 = -1 - t0; + __time64_t repaired_t0 = -1 - t0; approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM); diff = approx_biennia - approx_requested_biennia; approx_abs_diff = diff < 0 ? -1 - diff : diff; @@ -533,7 +514,7 @@ __mktime_internal (struct tm *tp, for (direction = -1; direction <= 1; direction += 2) if (time_t_int_add_ok (t, delta * direction)) { - time_t ot = t + delta * direction; + __time64_t ot = t + delta * direction; struct tm otm; ranged_convert (convert, &ot, &otm); if (! isdst_differ (isdst, otm.tm_isdst)) @@ -575,11 +556,11 @@ __mktime_internal (struct tm *tp, offset in seconds. 'int' should be good enough for GNU code. We can't fix this unilaterally though, as other modules invoke __mktime_internal. */ -static time_t localtime_offset; +static __time64_t localtime_offset; -/* Convert *TP to a time_t value. */ -time_t -mktime (struct tm *tp) +/* Convert *TP to a __time64_t value. */ +__time64_t +__mktime64 (struct tm *tp) { #ifdef _LIBC /* POSIX.1 8.1.1 requires that whenever mktime() is called, the @@ -588,7 +569,22 @@ mktime (struct tm *tp) __tzset (); #endif - return __mktime_internal (tp, __localtime_r, &localtime_offset); + return __mktime_internal (tp, __localtime64_r, &localtime_offset); +} + +#ifdef weak_alias +weak_alias (__mktime64, __timelocal64) +#endif + +/* The 32-bit-time wrapper. */ +time_t +mktime (struct tm *tp) +{ + __time64_t t64 = __mktime64 (tp); + if (fits_in_time_t (t64)) + return (time_t) t64; + __set_errno (EOVERFLOW); + return -1; } #ifdef weak_alias diff --git a/time/timegm.c b/time/timegm.c index fb720e2d7d..b36fef2320 100644 --- a/time/timegm.c +++ b/time/timegm.c @@ -33,15 +33,27 @@ # include # undef __gmtime_r # define __gmtime_r gmtime_r -time_t __mktime_internal (struct tm *, - struct tm * (*) (time_t const *, struct tm *), - time_t *); +time64_t __mktime_internal (struct tm *, + struct tm * (*) (__time64_t const *, struct tm *), + __time64_t *); #endif +# include +# include + +__time64_t +__timegm64 (struct tm *tmp) +{ + static __time64_t gmtime_offset; + tmp->tm_isdst = 0; + return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset); +} time_t timegm (struct tm *tmp) { - static time_t gmtime_offset; - tmp->tm_isdst = 0; - return __mktime_internal (tmp, __gmtime_r, &gmtime_offset); + __time64_t t64 = __timegm64(tmp); + if (fits_in_time_t (t64)) + return (time_t) t64; + __set_errno (EOVERFLOW); + return -1; }