From patchwork Wed Feb 27 11:20:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Majewski X-Patchwork-Id: 31620 Received: (qmail 61728 invoked by alias); 27 Feb 2019 11:21:06 -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 61711 invoked by uid 89); 27 Feb 2019 11:21:05 -0000 Authentication-Results: sourceware.org; auth=none 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=H*r:192.168.8, timer X-HELO: mail-out.m-online.net From: Lukasz Majewski To: libc-alpha@sourceware.org Cc: Paul Eggert , Joseph Myers Subject: [PATCH v2 1/2] Y2038: make __mktime_internal compatible with __time64_t Date: Wed, 27 Feb 2019 12:20:41 +0100 Message-Id: <20190227112042.1794-1-lukma@denx.de> From: Paul Eggert This implies also making its callers 64-bit-time compatible (these are mktime/localtime and timegm) and providing wrappers for 32-bit-time userland to call. Tested with 'make check' on x86_64-linux-gnu and i686-linux.gnu. * include/time.h (__mktime64): Add prototype. * include/time.h (__localtime64): Likewise. * include/time.h (fits_in_time_t): New static function. * time/mktime.c (__mktime64): New function. * time/timegm.c (__timegm64): Likewise. * time/mktime.c (mktime) [__TIMESIZE]: New wrapper function. * time/timegm.c (timegm) [__TIMESIZE]: Likewise. --- Applicable on top of the newest glibc's master branch SH1: aa0e46636a5b71b609a41e9ab97134cd76ac1522 Tested with make check for x86_64, i585 and ARM setups. Changes for v2: Add missing space (__set_errno (EOVERFLOW)) in src/time/timegm.c Add missing space (__set_errno (EOVERFLOW)) in src/time/mktime.c --- include/time.h | 36 +++++++++++++++++++++----- time/mktime.c | 80 ++++++++++++++++++++++++++++++++++++++++++---------------- time/timegm.c | 27 +++++++++++++++++++- 3 files changed, 114 insertions(+), 29 deletions(-) diff --git a/include/time.h b/include/time.h index 61dd9e180b..34e5e04820 100644 --- a/include/time.h +++ b/include/time.h @@ -3,6 +3,7 @@ #ifndef _ISOMAC # include +# include extern __typeof (strftime_l) __strftime_l; libc_hidden_proto (__strftime_l) @@ -16,6 +17,21 @@ libc_hidden_proto (localtime) libc_hidden_proto (strftime) libc_hidden_proto (strptime) +#if __TIMESIZE == 64 +# define __timegm64 timegm +# define __mktime64 mktime +# define __timelocal64 timelocal +#else +extern __time64_t __timegm64 (struct tm *__tp) __THROW; +extern __time64_t __mktime64 (struct tm *__tp) __THROW; +/* Another name for `__mktime64'. */ +extern __time64_t __timelocal64 (struct tm *__tp) __THROW; + +libc_hidden_proto (__mktime64) +libc_hidden_proto (__timelocal64) +#endif + + extern __typeof (clock_getres) __clock_getres; extern __typeof (clock_gettime) __clock_gettime; libc_hidden_proto (__clock_gettime) @@ -49,13 +65,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. Record next guess for localtime-gmtime offset in *OFFSET. */ -extern time_t __mktime_internal (struct tm *__tp, - struct tm *(*__func) (const time_t *, - struct tm *), - long int *__offset) attribute_hidden; +extern __time64_t __mktime_internal (struct tm *__tp, + struct tm *(*__func) (const __time64_t *, + struct tm *), + long int *__offset) attribute_hidden; #if __TIMESIZE == 64 # define __ctime64 ctime @@ -155,5 +171,13 @@ extern double __difftime (time_t time1, time_t time0); actual clock ID. */ #define CLOCK_IDFIELD_SIZE 3 +/* Check whether a time64_t value fits in a time_t. */ +static inline bool +fits_in_time_t (__time64_t t64) +{ + time_t t = t64; + return t == t64; +} + #endif #endif diff --git a/time/mktime.c b/time/mktime.c index af43a6950d..5d3644e213 100644 --- a/time/mktime.c +++ b/time/mktime.c @@ -112,11 +112,11 @@ my_tzset (void) added to them, and then with another timestamp added, without worrying about overflow. - Much of the code uses long_int to represent time_t values, to - lessen the hassle of dealing with platforms where time_t is + Much of the code uses long_int to represent __time64_t values, to + lessen the hassle of dealing with platforms where __time64_t is unsigned, and because long_int should suffice to represent all - time_t values that mktime can generate even on platforms where - time_t is excessively wide. */ + __time64_t values that mktime can generate even on platforms where + __time64_t is excessively wide. */ #if INT_MAX <= LONG_MAX / 4 / 366 / 24 / 60 / 60 typedef long int long_int; @@ -144,16 +144,17 @@ shr (long_int a, int b) : a / (one << b) - (a % (one << b) < 0)); } -/* Bounds for the intersection of time_t and long_int. */ +/* Bounds for the intersection of __time64_t and long_int. */ static long_int const mktime_min - = ((TYPE_SIGNED (time_t) && TYPE_MINIMUM (time_t) < TYPE_MINIMUM (long_int)) - ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (time_t)); + = ((TYPE_SIGNED (__time64_t) + && TYPE_MINIMUM (__time64_t) < TYPE_MINIMUM (long_int)) + ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (__time64_t)); static long_int const mktime_max - = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t) - ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t)); + = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (__time64_t) + ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (__time64_t)); -verify (TYPE_IS_INTEGER (time_t)); +verify (TYPE_IS_INTEGER (__time64_t)); #define EPOCH_YEAR 1970 #define TM_YEAR_BASE 1900 @@ -252,23 +253,23 @@ tm_diff (long_int year, long_int yday, int hour, int min, int sec, } /* Use CONVERT to convert T to a struct tm value in *TM. T must be in - range for time_t. Return TM if successful, NULL (setting errno) on + range for __time64_t. Return TM if successful, NULL (setting errno) on failure. */ static struct tm * -convert_time (struct tm *(*convert) (const time_t *, struct tm *), +convert_time (struct tm *(*convert) (const __time64_t *, struct tm *), long_int t, struct tm *tm) { - time_t x = t; + __time64_t x = t; return convert (&x, tm); } /* 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. - A value is in range if it fits in both time_t and long_int. + A value is in range if it fits in both __time64_t and long_int. Return TP on success, NULL (setting errno) on failure. */ static struct tm * -ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), +ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *), long_int *t, struct tm *tp) { long_int t1 = (*t < mktime_min ? mktime_min @@ -310,7 +311,7 @@ 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. @@ -318,9 +319,9 @@ ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), If successful, set *TP to the canonicalized struct tm; otherwise leave *TP alone, return ((time_t) -1) and set errno. 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 *), + struct tm *(*convert) (const __time64_t *, struct tm *), mktime_offset_t *offset) { struct tm tm; @@ -520,10 +521,13 @@ __mktime_internal (struct tm *tp, #if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS -/* 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) { + __time64_t t64; + time_t t; + struct tm tp0 = *tp; /* POSIX.1 8.1.1 requires that whenever mktime() is called, the time zone names contained in the external variable 'tzname' shall be set as if the tzset() function had been called. */ @@ -531,7 +535,15 @@ mktime (struct tm *tp) # if defined _LIBC || NEED_MKTIME_WORKING static mktime_offset_t localtime_offset; - return __mktime_internal (tp, __localtime_r, &localtime_offset); + t64 = __mktime_internal (&tp0, __localtime64_r, &localtime_offset); + t = t64; + if (t != t64) + { + __set_errno (EOVERFLOW); + return -1; + } + *tp = tp0; + return t; # else # undef mktime return mktime (tp); @@ -540,6 +552,28 @@ mktime (struct tm *tp) #endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */ #ifdef weak_alias +weak_alias (__mktime64, __timelocal64) +#endif + +#ifdef _LIBC +libc_hidden_def (__mktime64) +libc_hidden_weak (__timelocal64) +#endif + +#if __TIMESIZE != 64 + +/* The 32-bit-time wrapper. */ +time_t +mktime (struct tm *tp) +{ + __time64_t t64 = __mktime64 (tp); + if (fits_in_time_t (t64)) + return t64; + __set_errno (EOVERFLOW); + return -1; +} + +#ifdef weak_alias weak_alias (mktime, timelocal) #endif @@ -547,3 +581,5 @@ weak_alias (mktime, timelocal) libc_hidden_def (mktime) libc_hidden_weak (timelocal) #endif + +#endif diff --git a/time/timegm.c b/time/timegm.c index bfd36d0255..0cbe60a7fe 100644 --- a/time/timegm.c +++ b/time/timegm.c @@ -22,13 +22,38 @@ #endif #include +#include #include "mktime-internal.h" +#include + +__time64_t +__timegm64 (struct tm *tmp) +{ + static long int gmtime_offset; + tmp->tm_isdst = 0; + return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset); +} + +#if __TIMESIZE != 64 time_t timegm (struct tm *tmp) { + time_t t; + __time64_t t64; + struct tm tmp0 = *tmp; static mktime_offset_t gmtime_offset; tmp->tm_isdst = 0; - return __mktime_internal (tmp, __gmtime_r, &gmtime_offset); + t64 = __mktime_internal (&tmp0, __gmtime64_r, &gmtime_offset); + t = t64; + if (t != t64) + { + __set_errno (EOVERFLOW); + return -1; + } + *tmp = tmp0; + return t; } + +#endif