From: Paul Eggert Date: Sun, 1 May 2016 18:44:51 +0000 (-0700) Subject: Port mktime_internal offset to unsigned time_t X-Git-Tag: v1.0~6755 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=b28c2cda0e90ccad85a99c48dded5624e39d28ac;p=gnulib.git Port mktime_internal offset to unsigned time_t This avoids some assumptions about wraparound arithmetic on signed integer overflow. * lib/mktime-internal.h (mktime_offset_t): New type. (mktime_internal): Use it in decl. * lib/mktime.c, lib/timegm.c (mktime_offset_t) [_LIBC]: New type. * lib/mktime.c (__mktime_internal, localtime_offset): * lib/timegm.c (timegm): Use it. * m4/mktime.m4 (gl_TIME_T_IS_SIGNED): New macro. (gl_FUNC_MKTIME): Require it. --- diff --git a/ChangeLog b/ChangeLog index 09c302cb9d..4b406cc283 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2016-05-01 Paul Eggert + + Port mktime_internal offset to unsigned time_t + This avoids some assumptions about wraparound arithmetic on + signed integer overflow. + * lib/mktime-internal.h (mktime_offset_t): New type. + (mktime_internal): Use it in decl. + * lib/mktime.c, lib/timegm.c (mktime_offset_t) [_LIBC]: New type. + * lib/mktime.c (__mktime_internal, localtime_offset): + * lib/timegm.c (timegm): Use it. + * m4/mktime.m4 (gl_TIME_T_IS_SIGNED): New macro. + (gl_FUNC_MKTIME): Require it. + 2016-04-27 Paul Eggert xstrtol: prohibit monstrosities like "1bB" diff --git a/lib/mktime-internal.h b/lib/mktime-internal.h index 4287acf4ac..0c9e204050 100644 --- a/lib/mktime-internal.h +++ b/lib/mktime-internal.h @@ -1,4 +1,37 @@ +/* mktime variant that also uses an offset guess + + 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 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 + . */ + #include + +/* mktime_offset_t is a signed type wide enough to hold a UTC offset + in seconds, and used as part of the type of the offset-guess + argument to mktime_internal. Use time_t on platforms where time_t + is signed, to be compatible with platforms like BeOS that export + this implementation detail of mktime. On platforms where time_t is + unsigned, GNU and POSIX code can assume 'int' is at least 32 bits + which is wide enough for a UTC offset. */ + +#if TIME_T_IS_SIGNED +typedef time_t mktime_offset_t; +#else +typedef int mktime_offset_t; +#endif + time_t mktime_internal (struct tm *, struct tm * (*) (time_t const *, struct tm *), - time_t *); + mktime_offset_t *); diff --git a/lib/mktime.c b/lib/mktime.c index 627b1a38d3..4f4f0bf34c 100644 --- a/lib/mktime.c +++ b/lib/mktime.c @@ -120,7 +120,9 @@ const unsigned short int __mon_yday[2][13] = }; -#ifndef _LIBC +#ifdef _LIBC +typedef time_t mktime_offset_t; +#else /* 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. @@ -296,7 +298,7 @@ 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; struct tm tm; @@ -518,11 +520,7 @@ __mktime_internal (struct tm *tp, } -/* 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; +static mktime_offset_t localtime_offset; /* Convert *TP to a time_t value. */ time_t diff --git a/lib/timegm.c b/lib/timegm.c index 1f567ec63d..ee9e497827 100644 --- a/lib/timegm.c +++ b/lib/timegm.c @@ -22,7 +22,9 @@ #include -#ifndef _LIBC +#ifdef _LIBC +typedef time_t mktime_offset_t; +#else # undef __gmtime_r # define __gmtime_r gmtime_r # define __mktime_internal mktime_internal @@ -32,7 +34,7 @@ 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); } diff --git a/m4/mktime.m4 b/m4/mktime.m4 index 78f16ba66a..5a0f2d88ab 100644 --- a/m4/mktime.m4 +++ b/m4/mktime.m4 @@ -1,4 +1,4 @@ -# serial 25 +# serial 26 dnl Copyright (C) 2002-2003, 2005-2007, 2009-2016 Free Software Foundation, dnl Inc. dnl This file is free software; the Free Software Foundation @@ -7,9 +7,24 @@ dnl with or without modifications, as long as this notice is preserved. dnl From Jim Meyering. +AC_DEFUN([gl_TIME_T_IS_SIGNED], +[ + AC_CACHE_CHECK([whether time_t is signed], + [gl_cv_time_t_is_signed], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include + char time_t_signed[(time_t) -1 < 0 ? 1 : -1];]])], + [gl_cv_time_t_is_signed=yes], + [gl_cv_time_t_is_signed=no])]) + if test $gl_cv_time_t_is_signed = yes; then + AC_DEFINE([TIME_T_IS_SIGNED], [1], [Define to 1 if time_t is signed.]) + fi +]) + AC_DEFUN([gl_FUNC_MKTIME], [ AC_REQUIRE([gl_HEADER_TIME_H_DEFAULTS]) + AC_REQUIRE([gl_TIME_T_IS_SIGNED]) dnl We don't use AC_FUNC_MKTIME any more, because it is no longer maintained dnl in Autoconf and because it invokes AC_LIBOBJ. @@ -169,7 +184,6 @@ main () time_t t, delta; int i, j; int time_t_signed_magnitude = (time_t) ~ (time_t) 0 < (time_t) -1; - int time_t_signed = ! ((time_t) 0 < (time_t) -1); #if HAVE_DECL_ALARM /* This test makes some buggy mktime implementations loop. @@ -179,11 +193,11 @@ main () alarm (60); #endif - time_t_max = (! time_t_signed + time_t_max = (! TIME_T_IS_SIGNED ? (time_t) -1 : ((((time_t) 1 << (sizeof (time_t) * CHAR_BIT - 2)) - 1) * 2 + 1)); - time_t_min = (! time_t_signed + time_t_min = (! TIME_T_IS_SIGNED ? (time_t) 0 : time_t_signed_magnitude ? ~ (time_t) 0