From patchwork Sun Sep 9 16:06:24 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Paul Eggert X-Patchwork-Id: 29281 Received: (qmail 23779 invoked by alias); 9 Sep 2018 16:06:50 -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 23762 invoked by uid 89); 9 Sep 2018 16:06:50 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-24.7 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_PASS autolearn=ham version=3.3.2 spammy=singapore, Singapore, occur, practical X-HELO: zimbra.cs.ucla.edu From: Paul Eggert To: libc-alpha@sourceware.org Cc: Paul Eggert Subject: [PATCH 1/2] Merge mktime, timegm from upstream Gnulib Date: Sun, 9 Sep 2018 09:06:24 -0700 Message-Id: <20180909160625.15214-1-eggert@cs.ucla.edu> MIME-Version: 1.0 [BZ #23603][BZ #16346] This fixes some obscure problems with integer overflow. Although it looks scary, it is almost all a byte-for-byte copy from Gnulib, and the Gnulib code has been tested reasonably well. * include/intprops.h: New file, copied from Gnulib. * include/verify.h, time/mktime-internal.h: New tiny files, simplified from Gnulib. * time/mktime.c: Copy from Gnulib. This has the following changes: Do not include config.h if DEBUG_MKTIME is nonzero. Include stdbool.h, intprops.h, verify.h. Include string.h only if needed. Include stdlib.h on MS-Windows. Include mktime-internal.h. (DEBUG_MKTIME): Default to 0, and simplify later uses. (NEED_MKTIME_INTERNAL, NEED_MKTIME_WINDOWS) (NEED_MKTIME_WORKING): Give default values to pacify -Wundef, which glibc uses. Default NEED_MKTIME_WORKING to DEBUG_MKTIME, to simplify later conditionals; default the others to zero. Use these conditionals to express only the code needed on the current platform. In uses of these conditionals, explicitly spell out how _LIBC affects things, so it’s easier to review from a glibc viewpoint. (WRAPV): Remove; no longer needed now that we have systematic overflow checking. (my_tzset, __tzset) [!_LIBC]: New function and macro, to better compartmentalize tzset issues. Move system-dependent tzsettish code here from mktime. (verify): Remove; now done by verify.h. All uses changed. (long_int): Use a more-conservative definition, to avoid integer overflow. (SHR): Remove, replacing with ... (shr): New function, which means we needn’t worry about side effects in args, and conversion analysis is simpler. (TYPE_IS_INTEGER, TYPE_TWOS_COMPLEMENT, TYPE_SIGNED, TYPE_MINIMUM) (TYPE_MAXIMUM, TIME_T_MIN, TIME_T_MAX, TIME_T_MIDPOINT) (time_t_avg, time_t_add_ok): Remove. (mktime_min, mktime_max): New constants. (leapyear, isdst_differ): Use bool for booleans. (ydhms_diff, guess_time_tm, ranged_convert, __mktime_internal): Use long_int, not time_t, for mktime differences. (long_int_avg): New function, replacing time_t_avg. INT_ADD_WRAPV replaces time_t_add_ok. (guess_time_tm): 6th arg is now long_int, not time_t const *. All uses changed. (convert_time): New function. (ranged_convert): Use it. (__mktime_internal): Last arg now points to mktime_offset_t, not time_t. All uses changed. This is a no-op on glibc, where mktime_offset_t is always time_t. Use int, not time_t, for UTC offset guess. Directly check for integer overflow instead of using a heuristic that works only 99.9...% of the time. Access *OFFSET only once, to avoid an unlikely race if the compiler delays a load and if this cascades into a signed integer overflow. (mktime): Move tzsettish code to my_tzset, and move localtime_offset to within mktime so that it doesn’t need a separate ifdef. (main) [DEBUG_MKTIME]: Speed up by using localtime_r instead of localtime. * time/timegm.c: Copy from Gnulib. This has the following changes: Include mktime-internal.h. [!_LIBC]: Include config.h and time.h. Do not include timegm.h or time_r.h. Make __mktime_internal a macro, and include mktime-internal.h to get its declaration. (timegm): Temporary is now mktime_offset_t, not time_t. This affects only Gnulib. --- ChangeLog | 70 ++++++ include/intprops.h | 455 ++++++++++++++++++++++++++++++++++ include/verify.h | 2 + time/mktime-internal.h | 1 + time/mktime.c | 545 +++++++++++++++++++---------------------- time/timegm.c | 23 +- 6 files changed, 780 insertions(+), 316 deletions(-) create mode 100644 include/intprops.h create mode 100644 include/verify.h create mode 100644 time/mktime-internal.h diff --git a/ChangeLog b/ChangeLog index 611caf9bd8..68f6728290 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,73 @@ +2018-09-09 Paul Eggert + + Merge mktime, timegm from upstream Gnulib + [BZ #23603][BZ #16346] + This fixes some obscure problems with integer overflow. + Although it looks scary, it is almost all a byte-for-byte copy + from Gnulib, and the Gnulib code has been tested reasonably well. + * include/intprops.h: New file, copied from Gnulib. + * include/verify.h, time/mktime-internal.h: + New tiny files, simplified from Gnulib. + * time/mktime.c: Copy from Gnulib. This has the following changes: + Do not include config.h if DEBUG_MKTIME is nonzero. + Include stdbool.h, intprops.h, verify.h. + Include string.h only if needed. + Include stdlib.h on MS-Windows. + Include mktime-internal.h. + (DEBUG_MKTIME): Default to 0, and simplify later uses. + (NEED_MKTIME_INTERNAL, NEED_MKTIME_WINDOWS) + (NEED_MKTIME_WORKING): Give default values to pacify -Wundef, + which glibc uses. Default NEED_MKTIME_WORKING to DEBUG_MKTIME, to + simplify later conditionals; default the others to zero. Use + these conditionals to express only the code needed on the current + platform. In uses of these conditionals, explicitly spell out how + _LIBC affects things, so it’s easier to review from a glibc + viewpoint. + (WRAPV): Remove; no longer needed now that we have + systematic overflow checking. + (my_tzset, __tzset) [!_LIBC]: New function and macro, to better + compartmentalize tzset issues. Move system-dependent tzsettish + code here from mktime. + (verify): Remove; now done by verify.h. All uses changed. + (long_int): Use a more-conservative definition, to avoid + integer overflow. + (SHR): Remove, replacing with ... + (shr): New function, which means we needn’t worry about side + effects in args, and conversion analysis is simpler. + (TYPE_IS_INTEGER, TYPE_TWOS_COMPLEMENT, TYPE_SIGNED, TYPE_MINIMUM) + (TYPE_MAXIMUM, TIME_T_MIN, TIME_T_MAX, TIME_T_MIDPOINT) + (time_t_avg, time_t_add_ok): Remove. + (mktime_min, mktime_max): New constants. + (leapyear, isdst_differ): Use bool for booleans. + (ydhms_diff, guess_time_tm, ranged_convert, __mktime_internal): + Use long_int, not time_t, for mktime differences. + (long_int_avg): New function, replacing time_t_avg. + INT_ADD_WRAPV replaces time_t_add_ok. + (guess_time_tm): 6th arg is now long_int, not time_t const *. + All uses changed. + (convert_time): New function. + (ranged_convert): Use it. + (__mktime_internal): Last arg now points to mktime_offset_t, not + time_t. All uses changed. This is a no-op on glibc, where + mktime_offset_t is always time_t. Use int, not time_t, for UTC + offset guess. Directly check for integer overflow instead of + using a heuristic that works only 99.9...% of the time. + Access *OFFSET only once, to avoid an unlikely race if the + compiler delays a load and if this cascades into a signed integer + overflow. + (mktime): Move tzsettish code to my_tzset, and move + localtime_offset to within mktime so that it doesn’t + need a separate ifdef. + (main) [DEBUG_MKTIME]: Speed up by using localtime_r + instead of localtime. + * time/timegm.c: Copy from Gnulib. This has the following changes: + Include mktime-internal.h. + [!_LIBC]: Include config.h and time.h. Do not include + timegm.h or time_r.h. Make __mktime_internal a macro, + and include mktime-internal.h to get its declaration. + (timegm): Temporary is now mktime_offset_t, not time_t. + This affects only Gnulib. + 2018-09-06 Stefan Liebler * sysdeps/s390/fpu/libm-test-ulps: Regenerated. diff --git a/include/intprops.h b/include/intprops.h new file mode 100644 index 0000000000..9702aec4c6 --- /dev/null +++ b/include/intprops.h @@ -0,0 +1,455 @@ +/* intprops.h -- properties of integer types + + Copyright (C) 2001-2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +/* Written by Paul Eggert. */ + +#ifndef _GL_INTPROPS_H +#define _GL_INTPROPS_H + +#include + +/* Return a value with the common real type of E and V and the value of V. + Do not evaluate E. */ +#define _GL_INT_CONVERT(e, v) ((1 ? 0 : (e)) + (v)) + +/* Act like _GL_INT_CONVERT (E, -V) but work around a bug in IRIX 6.5 cc; see + . */ +#define _GL_INT_NEGATE_CONVERT(e, v) ((1 ? 0 : (e)) - (v)) + +/* 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 the real type T is signed. */ +#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) + +/* Return 1 if the real expression E, after promotion, has a + signed or floating type. Do not evaluate E. */ +#define EXPR_SIGNED(e) (_GL_INT_NEGATE_CONVERT (e, 1) < 0) + + +/* Minimum and maximum values for integer types and expressions. */ + +/* The width in bits of the integer type or expression T. + Do not evaluate T. + Padding bits are not supported; this is checked at compile-time below. */ +#define TYPE_WIDTH(t) (sizeof (t) * CHAR_BIT) + +/* The maximum and minimum values for the integer type T. */ +#define TYPE_MINIMUM(t) ((t) ~ TYPE_MAXIMUM (t)) +#define TYPE_MAXIMUM(t) \ + ((t) (! TYPE_SIGNED (t) \ + ? (t) -1 \ + : ((((t) 1 << (TYPE_WIDTH (t) - 2)) - 1) * 2 + 1))) + +/* The maximum and minimum values for the type of the expression E, + after integer promotion. E is not evaluated. */ +#define _GL_INT_MINIMUM(e) \ + (EXPR_SIGNED (e) \ + ? ~ _GL_SIGNED_INT_MAXIMUM (e) \ + : _GL_INT_CONVERT (e, 0)) +#define _GL_INT_MAXIMUM(e) \ + (EXPR_SIGNED (e) \ + ? _GL_SIGNED_INT_MAXIMUM (e) \ + : _GL_INT_NEGATE_CONVERT (e, 1)) +#define _GL_SIGNED_INT_MAXIMUM(e) \ + (((_GL_INT_CONVERT (e, 1) << (TYPE_WIDTH ((e) + 0) - 2)) - 1) * 2 + 1) + +/* Work around OpenVMS incompatibility with C99. */ +#if !defined LLONG_MAX && defined __INT64_MAX +# define LLONG_MAX __INT64_MAX +# define LLONG_MIN __INT64_MIN +#endif + +/* This include file assumes that signed types are two's complement without + padding bits; the above macros have undefined behavior otherwise. + If this is a problem for you, please let us know how to fix it for your host. + This assumption is tested by the intprops-tests module. */ + +/* Does the __typeof__ keyword work? This could be done by + 'configure', but for now it's easier to do it by hand. */ +#if (2 <= __GNUC__ \ + || (1210 <= __IBMC__ && defined __IBM__TYPEOF__) \ + || (0x5110 <= __SUNPRO_C && !__STDC__)) +# define _GL_HAVE___TYPEOF__ 1 +#else +# define _GL_HAVE___TYPEOF__ 0 +#endif + +/* Return 1 if the integer type or expression T might be signed. Return 0 + if it is definitely unsigned. This macro does not evaluate its argument, + and expands to an integer constant expression. */ +#if _GL_HAVE___TYPEOF__ +# define _GL_SIGNED_TYPE_OR_EXPR(t) TYPE_SIGNED (__typeof__ (t)) +#else +# define _GL_SIGNED_TYPE_OR_EXPR(t) 1 +#endif + +/* Bound on length of the string representing an unsigned integer + value representable in B bits. log10 (2.0) < 146/485. The + smallest value of B where this bound is not tight is 2621. */ +#define INT_BITS_STRLEN_BOUND(b) (((b) * 146 + 484) / 485) + +/* Bound on length of the string representing an integer type or expression T. + Subtract 1 for the sign bit if T is signed, and then add 1 more for + a minus sign if needed. + + Because _GL_SIGNED_TYPE_OR_EXPR sometimes returns 0 when its argument is + signed, this macro may overestimate the true bound by one byte when + applied to unsigned types of size 2, 4, 16, ... bytes. */ +#define INT_STRLEN_BOUND(t) \ + (INT_BITS_STRLEN_BOUND (TYPE_WIDTH (t) - _GL_SIGNED_TYPE_OR_EXPR (t)) \ + + _GL_SIGNED_TYPE_OR_EXPR (t)) + +/* Bound on buffer size needed to represent an integer type or expression T, + including the terminating null. */ +#define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1) + + +/* Range overflow checks. + + The INT__RANGE_OVERFLOW macros return 1 if the corresponding C + operators might not yield numerically correct answers due to + arithmetic overflow. They do not rely on undefined or + implementation-defined behavior. Their implementations are simple + and straightforward, but they are a bit harder to use than the + INT__OVERFLOW macros described below. + + Example usage: + + long int i = ...; + long int j = ...; + if (INT_MULTIPLY_RANGE_OVERFLOW (i, j, LONG_MIN, LONG_MAX)) + printf ("multiply would overflow"); + else + printf ("product is %ld", i * j); + + Restrictions on *_RANGE_OVERFLOW macros: + + These macros do not check for all possible numerical problems or + undefined or unspecified behavior: they do not check for division + by zero, for bad shift counts, or for shifting negative numbers. + + These macros may evaluate their arguments zero or multiple times, + so the arguments should not have side effects. The arithmetic + arguments (including the MIN and MAX arguments) must be of the same + integer type after the usual arithmetic conversions, and the type + must have minimum value MIN and maximum MAX. Unsigned types should + use a zero MIN of the proper type. + + These macros are tuned for constant MIN and MAX. For commutative + operations such as A + B, they are also tuned for constant B. */ + +/* Return 1 if A + B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. */ +#define INT_ADD_RANGE_OVERFLOW(a, b, min, max) \ + ((b) < 0 \ + ? (a) < (min) - (b) \ + : (max) - (b) < (a)) + +/* Return 1 if A - B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. */ +#define INT_SUBTRACT_RANGE_OVERFLOW(a, b, min, max) \ + ((b) < 0 \ + ? (max) + (b) < (a) \ + : (a) < (min) + (b)) + +/* Return 1 if - A would overflow in [MIN,MAX] arithmetic. + See above for restrictions. */ +#define INT_NEGATE_RANGE_OVERFLOW(a, min, max) \ + ((min) < 0 \ + ? (a) < - (max) \ + : 0 < (a)) + +/* Return 1 if A * B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Avoid && and || as they tickle + bugs in Sun C 5.11 2010/08/13 and other compilers; see + . */ +#define INT_MULTIPLY_RANGE_OVERFLOW(a, b, min, max) \ + ((b) < 0 \ + ? ((a) < 0 \ + ? (a) < (max) / (b) \ + : (b) == -1 \ + ? 0 \ + : (min) / (b) < (a)) \ + : (b) == 0 \ + ? 0 \ + : ((a) < 0 \ + ? (a) < (min) / (b) \ + : (max) / (b) < (a))) + +/* Return 1 if A / B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Do not check for division by zero. */ +#define INT_DIVIDE_RANGE_OVERFLOW(a, b, min, max) \ + ((min) < 0 && (b) == -1 && (a) < - (max)) + +/* Return 1 if A % B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Do not check for division by zero. + Mathematically, % should never overflow, but on x86-like hosts + INT_MIN % -1 traps, and the C standard permits this, so treat this + as an overflow too. */ +#define INT_REMAINDER_RANGE_OVERFLOW(a, b, min, max) \ + INT_DIVIDE_RANGE_OVERFLOW (a, b, min, max) + +/* Return 1 if A << B would overflow in [MIN,MAX] arithmetic. + See above for restrictions. Here, MIN and MAX are for A only, and B need + not be of the same type as the other arguments. The C standard says that + behavior is undefined for shifts unless 0 <= B < wordwidth, and that when + A is negative then A << B has undefined behavior and A >> B has + implementation-defined behavior, but do not check these other + restrictions. */ +#define INT_LEFT_SHIFT_RANGE_OVERFLOW(a, b, min, max) \ + ((a) < 0 \ + ? (a) < (min) >> (b) \ + : (max) >> (b) < (a)) + +/* True if __builtin_add_overflow (A, B, P) works when P is non-null. */ +#if 5 <= __GNUC__ && !defined __ICC +# define _GL_HAS_BUILTIN_OVERFLOW 1 +#else +# define _GL_HAS_BUILTIN_OVERFLOW 0 +#endif + +/* True if __builtin_add_overflow_p (A, B, C) works. */ +#define _GL_HAS_BUILTIN_OVERFLOW_P (7 <= __GNUC__) + +/* The _GL*_OVERFLOW macros have the same restrictions as the + *_RANGE_OVERFLOW macros, except that they do not assume that operands + (e.g., A and B) have the same type as MIN and MAX. Instead, they assume + that the result (e.g., A + B) has that type. */ +#if _GL_HAS_BUILTIN_OVERFLOW_P +# define _GL_ADD_OVERFLOW(a, b, min, max) \ + __builtin_add_overflow_p (a, b, (__typeof__ ((a) + (b))) 0) +# define _GL_SUBTRACT_OVERFLOW(a, b, min, max) \ + __builtin_sub_overflow_p (a, b, (__typeof__ ((a) - (b))) 0) +# define _GL_MULTIPLY_OVERFLOW(a, b, min, max) \ + __builtin_mul_overflow_p (a, b, (__typeof__ ((a) * (b))) 0) +#else +# define _GL_ADD_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? INT_ADD_RANGE_OVERFLOW (a, b, min, max) \ + : (a) < 0 ? (b) <= (a) + (b) \ + : (b) < 0 ? (a) <= (a) + (b) \ + : (a) + (b) < (b)) +# define _GL_SUBTRACT_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? INT_SUBTRACT_RANGE_OVERFLOW (a, b, min, max) \ + : (a) < 0 ? 1 \ + : (b) < 0 ? (a) - (b) <= (a) \ + : (a) < (b)) +# define _GL_MULTIPLY_OVERFLOW(a, b, min, max) \ + (((min) == 0 && (((a) < 0 && 0 < (b)) || ((b) < 0 && 0 < (a)))) \ + || INT_MULTIPLY_RANGE_OVERFLOW (a, b, min, max)) +#endif +#define _GL_DIVIDE_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? (b) == _GL_INT_NEGATE_CONVERT (min, 1) && (a) < - (max) \ + : (a) < 0 ? (b) <= (a) + (b) - 1 \ + : (b) < 0 && (a) + (b) <= (a)) +#define _GL_REMAINDER_OVERFLOW(a, b, min, max) \ + ((min) < 0 ? (b) == _GL_INT_NEGATE_CONVERT (min, 1) && (a) < - (max) \ + : (a) < 0 ? (a) % (b) != ((max) - (b) + 1) % (b) \ + : (b) < 0 && ! _GL_UNSIGNED_NEG_MULTIPLE (a, b, max)) + +/* Return a nonzero value if A is a mathematical multiple of B, where + A is unsigned, B is negative, and MAX is the maximum value of A's + type. A's type must be the same as (A % B)'s type. Normally (A % + -B == 0) suffices, but things get tricky if -B would overflow. */ +#define _GL_UNSIGNED_NEG_MULTIPLE(a, b, max) \ + (((b) < -_GL_SIGNED_INT_MAXIMUM (b) \ + ? (_GL_SIGNED_INT_MAXIMUM (b) == (max) \ + ? (a) \ + : (a) % (_GL_INT_CONVERT (a, _GL_SIGNED_INT_MAXIMUM (b)) + 1)) \ + : (a) % - (b)) \ + == 0) + +/* Check for integer overflow, and report low order bits of answer. + + The INT__OVERFLOW macros return 1 if the corresponding C operators + might not yield numerically correct answers due to arithmetic overflow. + The INT__WRAPV macros also store the low-order bits of the answer. + These macros work correctly on all known practical hosts, and do not rely + on undefined behavior due to signed arithmetic overflow. + + Example usage, assuming A and B are long int: + + if (INT_MULTIPLY_OVERFLOW (a, b)) + printf ("result would overflow\n"); + else + printf ("result is %ld (no overflow)\n", a * b); + + Example usage with WRAPV flavor: + + long int result; + bool overflow = INT_MULTIPLY_WRAPV (a, b, &result); + printf ("result is %ld (%s)\n", result, + overflow ? "after overflow" : "no overflow"); + + Restrictions on these macros: + + These macros do not check for all possible numerical problems or + undefined or unspecified behavior: they do not check for division + by zero, for bad shift counts, or for shifting negative numbers. + + These macros may evaluate their arguments zero or multiple times, so the + arguments should not have side effects. + + The WRAPV macros are not constant expressions. They support only + +, binary -, and *. The result type must be signed. + + These macros are tuned for their last argument being a constant. + + Return 1 if the integer expressions A * B, A - B, -A, A * B, A / B, + A % B, and A << B would overflow, respectively. */ + +#define INT_ADD_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_ADD_OVERFLOW) +#define INT_SUBTRACT_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_SUBTRACT_OVERFLOW) +#if _GL_HAS_BUILTIN_OVERFLOW_P +# define INT_NEGATE_OVERFLOW(a) INT_SUBTRACT_OVERFLOW (0, a) +#else +# define INT_NEGATE_OVERFLOW(a) \ + INT_NEGATE_RANGE_OVERFLOW (a, _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a)) +#endif +#define INT_MULTIPLY_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_MULTIPLY_OVERFLOW) +#define INT_DIVIDE_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_DIVIDE_OVERFLOW) +#define INT_REMAINDER_OVERFLOW(a, b) \ + _GL_BINARY_OP_OVERFLOW (a, b, _GL_REMAINDER_OVERFLOW) +#define INT_LEFT_SHIFT_OVERFLOW(a, b) \ + INT_LEFT_SHIFT_RANGE_OVERFLOW (a, b, \ + _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a)) + +/* Return 1 if the expression A B would overflow, + where OP_RESULT_OVERFLOW (A, B, MIN, MAX) does the actual test, + assuming MIN and MAX are the minimum and maximum for the result type. + Arguments should be free of side effects. */ +#define _GL_BINARY_OP_OVERFLOW(a, b, op_result_overflow) \ + op_result_overflow (a, b, \ + _GL_INT_MINIMUM (_GL_INT_CONVERT (a, b)), \ + _GL_INT_MAXIMUM (_GL_INT_CONVERT (a, b))) + +/* Store the low-order bits of A + B, A - B, A * B, respectively, into *R. + Return 1 if the result overflows. See above for restrictions. */ +#define INT_ADD_WRAPV(a, b, r) \ + _GL_INT_OP_WRAPV (a, b, r, +, __builtin_add_overflow, INT_ADD_OVERFLOW) +#define INT_SUBTRACT_WRAPV(a, b, r) \ + _GL_INT_OP_WRAPV (a, b, r, -, __builtin_sub_overflow, INT_SUBTRACT_OVERFLOW) +#define INT_MULTIPLY_WRAPV(a, b, r) \ + _GL_INT_OP_WRAPV (a, b, r, *, __builtin_mul_overflow, INT_MULTIPLY_OVERFLOW) + +/* Nonzero if this compiler has GCC bug 68193 or Clang bug 25390. See: + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68193 + https://llvm.org/bugs/show_bug.cgi?id=25390 + For now, assume all versions of GCC-like compilers generate bogus + warnings for _Generic. This matters only for older compilers that + lack __builtin_add_overflow. */ +#if __GNUC__ +# define _GL__GENERIC_BOGUS 1 +#else +# define _GL__GENERIC_BOGUS 0 +#endif + +/* Store the low-order bits of A B into *R, where OP specifies + the operation. BUILTIN is the builtin operation, and OVERFLOW the + overflow predicate. Return 1 if the result overflows. See above + for restrictions. */ +#if _GL_HAS_BUILTIN_OVERFLOW +# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) builtin (a, b, r) +#elif 201112 <= __STDC_VERSION__ && !_GL__GENERIC_BOGUS +# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) \ + (_Generic \ + (*(r), \ + signed char: \ + _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \ + signed char, SCHAR_MIN, SCHAR_MAX), \ + short int: \ + _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \ + short int, SHRT_MIN, SHRT_MAX), \ + int: \ + _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \ + int, INT_MIN, INT_MAX), \ + long int: \ + _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \ + long int, LONG_MIN, LONG_MAX), \ + long long int: \ + _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \ + long long int, LLONG_MIN, LLONG_MAX))) +#else +# define _GL_INT_OP_WRAPV(a, b, r, op, builtin, overflow) \ + (sizeof *(r) == sizeof (signed char) \ + ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \ + signed char, SCHAR_MIN, SCHAR_MAX) \ + : sizeof *(r) == sizeof (short int) \ + ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \ + short int, SHRT_MIN, SHRT_MAX) \ + : sizeof *(r) == sizeof (int) \ + ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned int, \ + int, INT_MIN, INT_MAX) \ + : _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow)) +# ifdef LLONG_MAX +# define _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow) \ + (sizeof *(r) == sizeof (long int) \ + ? _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \ + long int, LONG_MIN, LONG_MAX) \ + : _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long long int, \ + long long int, LLONG_MIN, LLONG_MAX)) +# else +# define _GL_INT_OP_WRAPV_LONGISH(a, b, r, op, overflow) \ + _GL_INT_OP_CALC (a, b, r, op, overflow, unsigned long int, \ + long int, LONG_MIN, LONG_MAX) +# endif +#endif + +/* Store the low-order bits of A B into *R, where the operation + is given by OP. Use the unsigned type UT for calculation to avoid + overflow problems. *R's type is T, with extrema TMIN and TMAX. + T must be a signed integer type. Return 1 if the result overflows. */ +#define _GL_INT_OP_CALC(a, b, r, op, overflow, ut, t, tmin, tmax) \ + (sizeof ((a) op (b)) < sizeof (t) \ + ? _GL_INT_OP_CALC1 ((t) (a), (t) (b), r, op, overflow, ut, t, tmin, tmax) \ + : _GL_INT_OP_CALC1 (a, b, r, op, overflow, ut, t, tmin, tmax)) +#define _GL_INT_OP_CALC1(a, b, r, op, overflow, ut, t, tmin, tmax) \ + ((overflow (a, b) \ + || (EXPR_SIGNED ((a) op (b)) && ((a) op (b)) < (tmin)) \ + || (tmax) < ((a) op (b))) \ + ? (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t), 1) \ + : (*(r) = _GL_INT_OP_WRAPV_VIA_UNSIGNED (a, b, op, ut, t), 0)) + +/* Return the low-order bits of A B, where the operation is given + by OP. Use the unsigned type UT for calculation to avoid undefined + behavior on signed integer overflow, and convert the result to type T. + UT is at least as wide as T and is no narrower than unsigned int, + T is two's complement, and there is no padding or trap representations. + Assume that converting UT to T yields the low-order bits, as is + done in all known two's-complement C compilers. E.g., see: + https://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html + + According to the C standard, converting UT to T yields an + implementation-defined result or signal for values outside T's + range. However, code that works around this theoretical problem + runs afoul of a compiler bug in Oracle Studio 12.3 x86. See: + https://lists.gnu.org/r/bug-gnulib/2017-04/msg00049.html + As the compiler bug is real, don't try to work around the + theoretical problem. */ + +#define _GL_INT_OP_WRAPV_VIA_UNSIGNED(a, b, op, ut, t) \ + ((t) ((ut) (a) op (ut) (b))) + +#endif /* _GL_INTPROPS_H */ diff --git a/include/verify.h b/include/verify.h new file mode 100644 index 0000000000..00e78d3f8c --- /dev/null +++ b/include/verify.h @@ -0,0 +1,2 @@ +/* Gnulib , simplified by assuming GCC 4.6 or later. */ +#define verify(R) _Static_assert (R, "verify (" #R ")") diff --git a/time/mktime-internal.h b/time/mktime-internal.h new file mode 100644 index 0000000000..ba1d9c7aab --- /dev/null +++ b/time/mktime-internal.h @@ -0,0 +1 @@ +typedef time_t mktime_offset_t; diff --git a/time/mktime.c b/time/mktime.c index 5f038a212f..4ff74901e4 100644 --- a/time/mktime.c +++ b/time/mktime.c @@ -15,13 +15,30 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ -/* Define this to have a standalone program to test this implementation of +/* Define this to 1 to have a standalone program to test this implementation of mktime. */ -/* #define DEBUG_MKTIME 1 */ +#ifndef DEBUG_MKTIME +# define DEBUG_MKTIME 0 +#endif -#ifndef _LIBC +/* The following macros influence what gets defined when this file is compiled: + + Macro/expression Which gnulib module This compilation unit + should define + + _LIBC (glibc proper) mktime + + NEED_MKTIME_WORKING mktime rpl_mktime + || NEED_MKTIME_WINDOWS + + NEED_MKTIME_INTERNAL mktime-internal mktime_internal + + DEBUG_MKTIME (defined manually) my_mktime, main + */ + +#if !defined _LIBC && !DEBUG_MKTIME # include #endif @@ -35,114 +52,128 @@ #include #include +#include +#include +#include -#include /* For the real memcpy prototype. */ +#include +#include -#if defined DEBUG_MKTIME && DEBUG_MKTIME +#if DEBUG_MKTIME # include -# include /* Make it work even if the system's libc has its own mktime routine. */ # undef mktime # define mktime my_mktime #endif /* DEBUG_MKTIME */ -/* Some of the code in this file assumes that signed integer overflow - silently wraps around. This assumption can't easily be programmed - around, nor can it be checked for portably at compile-time or - easily eliminated at run-time. - - Define WRAPV to 1 if the assumption is valid and if - #pragma GCC optimize ("wrapv") - does not trigger GCC bug 51793 - . - Otherwise, define it to 0; this forces the use of slower code that, - while not guaranteed by the C Standard, works on all production - platforms that we know about. */ -#ifndef WRAPV -# if (((__GNUC__ == 4 && 4 <= __GNUC_MINOR__) || 4 < __GNUC__) \ - && defined __GLIBC__) -# pragma GCC optimize ("wrapv") -# define WRAPV 1 -# else -# define WRAPV 0 +#ifndef NEED_MKTIME_INTERNAL +# define NEED_MKTIME_INTERNAL 0 +#endif +#ifndef NEED_MKTIME_WINDOWS +# define NEED_MKTIME_WINDOWS 0 +#endif +#ifndef NEED_MKTIME_WORKING +# define NEED_MKTIME_WORKING DEBUG_MKTIME +#endif + +#include "mktime-internal.h" + +#ifndef _LIBC +static void +my_tzset (void) +{ +# if NEED_MKTIME_WINDOWS + /* Rectify the value of the environment variable TZ. + There are four possible kinds of such values: + - Traditional US time zone names, e.g. "PST8PDT". Syntax: see + + - Time zone names based on geography, that contain one or more + slashes, e.g. "Europe/Moscow". + - Time zone names based on geography, without slashes, e.g. + "Singapore". + - Time zone names that contain explicit DST rules. Syntax: see + + The Microsoft CRT understands only the first kind. It produces incorrect + results if the value of TZ is of the other kinds. + But in a Cygwin environment, /etc/profile.d/tzset.sh sets TZ to a value + of the second kind for most geographies, or of the first kind in a few + other geographies. If it is of the second kind, neutralize it. For the + Microsoft CRT, an absent or empty TZ means the time zone that the user + has set in the Windows Control Panel. + If the value of TZ is of the third or fourth kind -- Cygwin programs + understand these syntaxes as well --, it does not matter whether we + neutralize it or not, since these values occur only when a Cygwin user + has set TZ explicitly; this case is 1. rare and 2. under the user's + responsibility. */ + const char *tz = getenv ("TZ"); + if (tz != NULL && strchr (tz, '/') != NULL) + _putenv ("TZ="); +# elif HAVE_TZSET + tzset (); # endif +} +# undef __tzset +# define __tzset() my_tzset () #endif -/* Verify a requirement at compile-time (unlike assert, which is runtime). */ -#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } +#if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL + +/* A signed type that can represent an integer number of years + multiplied by three times the number of seconds in a year. It is + needed when converting a tm_year value times the number of seconds + in a year. The factor of three comes because these products need + to be subtracted from each other, and sometimes with an offset + added to them, 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 + 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. */ -/* A signed type that is at least one bit wider than int. */ -#if INT_MAX <= LONG_MAX / 2 +#if INT_MAX <= LONG_MAX / 3 / 366 / 24 / 60 / 60 typedef long int long_int; #else typedef long long int long_int; #endif -verify (long_int_is_wide_enough, INT_MAX == INT_MAX * (long_int) 2 / 2); +verify (INT_MAX <= TYPE_MAXIMUM (long_int) / 3 / 366 / 24 / 60 / 60); /* Shift A right by B bits portably, by dividing A by 2**B and - truncating towards minus infinity. A and B should be free of side - effects, and B should be in the range 0 <= B <= INT_BITS - 2, where - INT_BITS is the number of useful bits in an int. GNU code can - assume that INT_BITS is at least 32. + truncating towards minus infinity. B should be in the range 0 <= B + <= LONG_INT_BITS - 2, where LONG_INT_BITS is the number of useful + bits in a long_int. LONG_INT_BITS is at least 32. ISO C99 says that A >> B is implementation-defined if A < 0. Some implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift right in the usual way when A < 0, so SHR falls back on division if ordinary A >> B doesn't seem to be the usual signed shift. */ -#define SHR(a, b) \ - ((-1 >> 1 == -1 \ - && (long_int) -1 >> 1 == -1 \ - && ((time_t) -1 >> 1 == -1 || ! TYPE_SIGNED (time_t))) \ - ? (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) -#endif -#ifndef TIME_T_MAX -# define TIME_T_MAX TYPE_MAXIMUM (time_t) -#endif -#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_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))); +static long_int +shr (long_int a, int b) +{ + long_int one = 1; + return (-one >> 1 == -1 + ? a >> b + : a / (one << b) - (a % (one << b) < 0)); +} + +/* Bounds for the intersection of time_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)); +static long_int const mktime_max + = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t) + ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t)); + +verify (TYPE_IS_INTEGER (time_t)); #define EPOCH_YEAR 1970 #define TM_YEAR_BASE 1900 -verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0); +verify (TM_YEAR_BASE % 100 == 0); -/* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */ -static int +/* Is YEAR + TM_YEAR_BASE a leap year? */ +static bool leapyear (long_int year) { /* Don't add YEAR to TM_YEAR_BASE, as that might overflow. @@ -166,20 +197,9 @@ const unsigned short int __mon_yday[2][13] = }; -#ifndef _LIBC -/* Portable standalone applications should supply a that - declares a POSIX-compliant localtime_r, for the benefit of older - implementations that lack localtime_r or have a nonstandard one. - See the gnulib time_r module for one way to implement this. */ -# undef __localtime_r -# define __localtime_r localtime_r -# define __mktime_internal mktime_internal -# include "mktime-internal.h" -#endif - -/* Return 1 if the values A and B differ according to the rules for - tm_isdst: A and B differ if one is zero and the other positive. */ -static int +/* Do the values A and B differ according to the rules for tm_isdst? + A and B differ if one is zero and the other positive. */ +static bool isdst_differ (int a, int b) { return (!a != !b) && (0 <= a) && (0 <= b); @@ -187,107 +207,68 @@ isdst_differ (int a, int b) /* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) - (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks - were not adjusted between the time stamps. + were not adjusted between the timestamps. The YEAR values uses the same numbering as TP->tm_year. Values - need not be in the usual range. However, YEAR1 must not be less - than 2 * INT_MIN or greater than 2 * INT_MAX. - - The result may overflow. It is the caller's responsibility to - detect overflow. */ + need not be in the usual range. However, YEAR1 must not overflow + when multiplied by three times the number of seconds in a year, and + likewise for YDAY1 and three times the number of seconds in a day. */ -static time_t +static long_int ydhms_diff (long_int year1, long_int yday1, int hour1, int min1, int sec1, int year0, int yday0, int hour0, int min0, int sec0) { - verify (C99_integer_division, -1 / 2 == 0); + verify (-1 / 2 == 0); /* Compute intervening leap days correctly even if year is negative. Take care to avoid integer overflow here. */ - int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3); - int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3); + int a4 = shr (year1, 2) + shr (TM_YEAR_BASE, 2) - ! (year1 & 3); + int b4 = shr (year0, 2) + shr (TM_YEAR_BASE, 2) - ! (year0 & 3); int a100 = a4 / 25 - (a4 % 25 < 0); int b100 = b4 / 25 - (b4 % 25 < 0); - int a400 = SHR (a100, 2); - int b400 = SHR (b100, 2); + int a400 = shr (a100, 2); + 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 - 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; + /* Compute the desired time without overflowing. */ + long_int years = year1 - year0; + long_int days = 365 * years + yday1 - yday0 + intervening_leap_days; + long_int hours = 24 * days + hour1 - hour0; + long_int minutes = 60 * hours + min1 - min0; + long_int 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) +/* Return the average of A and B, even if A + B would overflow. + Round toward positive infinity. */ +static long_int +long_int_avg (long_int a, long_int 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. */ -static int -time_t_add_ok (time_t a, time_t b) -{ - if (! TYPE_SIGNED (time_t)) - { - time_t sum = a + b; - return (sum < a) == (TIME_T_MIDPOINT <= b); - } - else if (WRAPV) - { - time_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; - } -} - -/* Return 1 if A + B does not overflow. */ -static int -time_t_int_add_ok (time_t a, int b) -{ - verify (int_no_wider_than_time_t, INT_MAX <= TIME_T_MAX); - if (WRAPV) - { - time_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; - } + return shr (a, 1) + shr (b, 1) + ((a | b) & 1); } /* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC), - assuming that *T corresponds to *TP and that no clock adjustments + 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 + Although T and the returned value are of type long_int, + they represent time_t values and must be in time_t range. + If TP is null, return a value not equal to T; this avoids false matches. + YEAR and YDAY must not be so large that multiplying them by three times the + number of seconds in a year (or day, respectively) would overflow long_int. + If the returned value would be out of range, yield the minimal or + maximal in-range value, except do not yield a value equal to T. */ +static long_int guess_time_tm (long_int year, long_int yday, int hour, int min, int sec, - const time_t *t, const struct tm *tp) + long_int 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); - if (time_t_add_ok (*t, d)) - return *t + d; + long_int result; + long_int d = ydhms_diff (year, yday, hour, min, sec, + tp->tm_year, tp->tm_yday, + tp->tm_hour, tp->tm_min, tp->tm_sec); + if (! INT_ADD_WRAPV (t, d, &result)) + return result; } /* Overflow occurred one way or another. Return the nearest result @@ -295,32 +276,51 @@ guess_time_tm (long_int year, long_int yday, int hour, int min, int sec, if the actual difference is nonzero, as that would cause a false 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)); + return (t < long_int_avg (mktime_min, mktime_max) + ? (t <= mktime_min + 1 ? t + 1 : mktime_min) + : (mktime_max - 1 <= t ? t - 1 : mktime_max)); +} + +/* 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 if T is out of + range for CONVERT. */ +static struct tm * +convert_time (struct tm *(*convert) (const time_t *, struct tm *), + long_int t, struct tm *tm) +{ + time_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. */ + 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. */ static struct tm * ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), - time_t *t, struct tm *tp) + long_int *t, struct tm *tp) { - struct tm *r = convert (t, tp); + struct tm *r; + if (*t < mktime_min) + *t = mktime_min; + else if (mktime_max < *t) + *t = mktime_max; + r = convert_time (convert, *t, tp); if (!r && *t) { - time_t bad = *t; - time_t ok = 0; + long_int bad = *t; + long_int ok = 0; - /* BAD is a known unconvertible time_t, and OK is a known good one. + /* BAD is a known unconvertible value, 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)) + while (true) { - time_t mid = *t = time_t_avg (ok, bad); - r = convert (t, tp); + long_int mid = long_int_avg (ok, bad); + if (mid != ok && mid != bad) + break; + r = convert_time (convert, mid, tp); if (r) ok = mid; else @@ -331,8 +331,7 @@ ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), { /* The last conversion attempt failed; revert to the most recent successful attempt. */ - *t = ok; - r = convert (t, tp); + r = convert_time (convert, ok, tp); } } @@ -349,9 +348,9 @@ ranged_convert (struct tm *(*convert) (const time_t *, struct tm *), time_t __mktime_internal (struct tm *tp, struct tm *(*convert) (const time_t *, struct tm *), - time_t *offset) + mktime_offset_t *offset) { - time_t t, gt, t0, t1, t2; + long_int t, gt, t0, t1, t2, dt; struct tm tm; /* The maximum number of probes (calls to CONVERT) should be enough @@ -381,9 +380,7 @@ __mktime_internal (struct tm *tp, long_int year = lyear_requested + mon_years; /* The other values need not be in range: - the remaining code handles minor overflows correctly, - assuming int and time_t arithmetic wraps around. - Major overflows are caught at the end. */ + the remaining code handles overflows correctly. */ /* Calculate day of year from year, month, and day of month. The result need not be in range. */ @@ -393,7 +390,8 @@ __mktime_internal (struct tm *tp, long_int lmday = mday; long_int yday = mon_yday + lmday; - time_t guessed_offset = *offset; + mktime_offset_t off = *offset; + int negative_offset_guess; int sec_requested = sec; @@ -410,71 +408,14 @@ __mktime_internal (struct tm *tp, /* Invert CONVERT by probing. First assume the same offset as last time. */ + INT_SUBTRACT_WRAPV (0, off, &negative_offset_guess); 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) - { - /* 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 - that is bounded by a small value. */ - - /* Approximate log base 2 of the number of time units per - biennium. A biennium is 2 years; use this unit instead of - years to avoid integer overflow. For example, 2 average - Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds, - which is 63113904 seconds, and rint (log2 (63113904)) is - 26. */ - int ALOG2_SECONDS_PER_BIENNIUM = 26; - int ALOG2_MINUTES_PER_BIENNIUM = 20; - int ALOG2_HOURS_PER_BIENNIUM = 14; - int ALOG2_DAYS_PER_BIENNIUM = 10; - int LOG2_YEARS_PER_BIENNIUM = 1; - - int approx_requested_biennia = - (SHR (year_requested, LOG2_YEARS_PER_BIENNIUM) - - SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM) - + SHR (mday, ALOG2_DAYS_PER_BIENNIUM) - + SHR (hour, ALOG2_HOURS_PER_BIENNIUM) - + SHR (min, ALOG2_MINUTES_PER_BIENNIUM) - + (LEAP_SECONDS_POSSIBLE - ? 0 - : SHR (sec, ALOG2_SECONDS_PER_BIENNIUM))); - - int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM); - 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 - 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 = - (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; - approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM); - diff = approx_biennia - approx_requested_biennia; - approx_abs_diff = diff < 0 ? -1 - diff : diff; - if (overflow_threshold < approx_abs_diff) - return -1; - guessed_offset += repaired_t0 - t0; - t0 = repaired_t0; - } - } + EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, negative_offset_guess); /* Repeatedly use the error to improve the guess. */ for (t = t1 = t2 = t0, dst2 = 0; - (gt = guess_time_tm (year, yday, hour, min, sec, &t, + (gt = guess_time_tm (year, yday, hour, min, sec, t, ranged_convert (convert, &t, &tm)), t != gt); t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0) @@ -531,65 +472,70 @@ __mktime_internal (struct tm *tp, for (delta = stride; delta < delta_bound; delta += stride) for (direction = -1; direction <= 1; direction += 2) - if (time_t_int_add_ok (t, delta * direction)) - { - time_t ot = t + delta * direction; - struct tm otm; - ranged_convert (convert, &ot, &otm); - if (! isdst_differ (isdst, otm.tm_isdst)) - { - /* We found the desired tm_isdst. - Extrapolate back to the desired time. */ - t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm); - ranged_convert (convert, &t, &tm); - goto offset_found; - } - } + { + long_int ot; + if (! INT_ADD_WRAPV (t, delta * direction, &ot)) + { + struct tm otm; + ranged_convert (convert, &ot, &otm); + if (! isdst_differ (isdst, otm.tm_isdst)) + { + /* We found the desired tm_isdst. + Extrapolate back to the desired time. */ + t = guess_time_tm (year, yday, hour, min, sec, ot, &otm); + ranged_convert (convert, &t, &tm); + goto offset_found; + } + } + } } offset_found: - *offset = guessed_offset + t - t0; + /* Set *OFFSET to the low-order bits of T - T0 - NEGATIVE_OFFSET_GUESS. + This is just a heuristic to speed up the next mktime call, and + correctness is unaffected if integer overflow occurs here. */ + INT_SUBTRACT_WRAPV (t, t0, &dt); + INT_SUBTRACT_WRAPV (dt, negative_offset_guess, offset); if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec) { /* Adjust time to reflect the tm_sec requested, not the normalized value. Also, repair any damage from a false match due to a leap second. */ - int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec; - if (! time_t_int_add_ok (t, sec_requested)) - return -1; - t1 = t + sec_requested; - if (! time_t_int_add_ok (t1, sec_adjustment)) - return -1; - t2 = t1 + sec_adjustment; - if (! convert (&t2, &tm)) + long_int sec_adjustment = sec == 0 && tm.tm_sec == 60; + sec_adjustment -= sec; + sec_adjustment += sec_requested; + if (INT_ADD_WRAPV (t, sec_adjustment, &t) + || ! (mktime_min <= t && t <= mktime_max) + || ! convert_time (convert, t, &tm)) return -1; - t = t2; } *tp = tm; return t; } +#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_INTERNAL */ -/* FIXME: This should use a signed type wide enough to hold any UTC - 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; +#if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS /* Convert *TP to a time_t value. */ time_t mktime (struct tm *tp) { -#ifdef _LIBC /* 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. */ __tzset (); -#endif +# if defined __LIBC || NEED_MKTIME_WORKING + static mktime_offset_t localtime_offset; return __mktime_internal (tp, __localtime_r, &localtime_offset); +# else +# undef mktime + return mktime (tp); +# endif } +#endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */ #ifdef weak_alias weak_alias (mktime, timelocal) @@ -600,7 +546,7 @@ libc_hidden_def (mktime) libc_hidden_weak (timelocal) #endif -#if defined DEBUG_MKTIME && DEBUG_MKTIME +#if DEBUG_MKTIME static int not_equal_tm (const struct tm *a, const struct tm *b) @@ -652,6 +598,14 @@ main (int argc, char **argv) time_t tk, tl, tl1; char trailer; + /* Sanity check, plus call tzset. */ + tl = 0; + if (! localtime (&tl)) + { + printf ("localtime (0) fails\n"); + status = 1; + } + if ((argc == 3 || argc == 4) && (sscanf (argv[1], "%d-%d-%d%c", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer) @@ -665,12 +619,7 @@ main (int argc, char **argv) tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]); tmk = tm; tl = mktime (&tmk); - lt = localtime (&tl); - if (lt) - { - tml = *lt; - lt = &tml; - } + lt = localtime_r (&tl, &tml); printf ("mktime returns %ld == ", (long int) tl); print_tm (&tmk); printf ("\n"); @@ -685,16 +634,16 @@ main (int argc, char **argv) if (argc == 4) for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1) { - lt = localtime (&tl); + lt = localtime_r (&tl, &tml); if (lt) { - tmk = tml = *lt; + tmk = tml; tk = mktime (&tmk); status |= check_result (tk, tmk, tl, &tml); } else { - printf ("localtime (%ld) yields 0\n", (long int) tl); + printf ("localtime_r (%ld) yields 0\n", (long int) tl); status = 1; } tl1 = tl + by; @@ -705,16 +654,16 @@ main (int argc, char **argv) for (tl = from; by < 0 ? to <= tl : tl <= to; tl = tl1) { /* Null benchmark. */ - lt = localtime (&tl); + lt = localtime_r (&tl, &tml); if (lt) { - tmk = tml = *lt; + tmk = tml; tk = tl; status |= check_result (tk, tmk, tl, &tml); } else { - printf ("localtime (%ld) yields 0\n", (long int) tl); + printf ("localtime_r (%ld) yields 0\n", (long int) tl); status = 1; } tl1 = tl + by; diff --git a/time/timegm.c b/time/timegm.c index fb720e2d7d..229fff23c6 100644 --- a/time/timegm.c +++ b/time/timegm.c @@ -17,31 +17,18 @@ License along with the GNU C Library; if not, see . */ -#ifdef HAVE_CONFIG_H +#ifndef _LIBC # include #endif -#ifdef _LIBC -# include -#else -# include "timegm.h" - -/* Portable standalone applications should supply a "time_r.h" that - declares a POSIX-compliant gmtime_r, for the benefit of older - implementations that lack gmtime_r or have a nonstandard one. - See the gnulib time_r module for one way to implement this. */ -# include -# undef __gmtime_r -# define __gmtime_r gmtime_r -time_t __mktime_internal (struct tm *, - struct tm * (*) (time_t const *, struct tm *), - time_t *); -#endif +#include + +#include "mktime-internal.h" time_t timegm (struct tm *tmp) { - static time_t gmtime_offset; + static mktime_offset_t gmtime_offset; tmp->tm_isdst = 0; return __mktime_internal (tmp, __gmtime_r, &gmtime_offset); }