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. --- ChangeLog | 13 +++++++++++++ lib/mktime-internal.h | 35 ++++++++++++++++++++++++++++++++++- lib/mktime.c | 12 +++++------- lib/timegm.c | 6 ++++-- m4/mktime.m4 | 22 ++++++++++++++++++---- 5 files changed, 74 insertions(+), 14 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 09c302c..4b406cc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2016-05-01 Paul Eggert <egg...@cs.ucla.edu> + + 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 <egg...@cs.ucla.edu> xstrtol: prohibit monstrosities like "1bB" diff --git a/lib/mktime-internal.h b/lib/mktime-internal.h index 4287acf..0c9e204 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 + <http://www.gnu.org/licenses/>. */ + #include <time.h> + +/* 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 627b1a3..4f4f0bf 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 <time.h> 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 1f567ec..ee9e497 100644 --- a/lib/timegm.c +++ b/lib/timegm.c @@ -22,7 +22,9 @@ #include <time.h> -#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 78f16ba..5a0f2d8 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 <time.h> + 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 -- 2.5.5