mirror of
https://frontier.innolan.net/rainlance/amiga-tz.git
synced 2026-09-19 06:14:37 +00:00
Avoid undefined behavior on integer overflow.
Problem reported by Alois Treindl in <http://mm.icann.org/pipermail/tz/2013-August/019493.html>. * localtime.c (truncate_time): New function. (localsub): Use it to avoid undefined behavior on integer overflow. * private.h (INTMAX_MAX, INTMAX_MIN, UINTMAX_MAX): New macros, for older platforms that lack them.
This commit is contained in:
+38
-12
@@ -1254,6 +1254,38 @@ tzset(void)
|
||||
settzname();
|
||||
}
|
||||
|
||||
/* Return T with any fractional part discarded. */
|
||||
static time_t
|
||||
truncate_time(time_t t)
|
||||
{
|
||||
/*
|
||||
** If time_t is floating-point, convert it to integer and
|
||||
** back; this discards the fraction. Avoid using <math.h>
|
||||
** functions, as we don't want to depend on <math.h>. Use <,
|
||||
** not <=, when comparing against *_MIN and *_MAX values, as
|
||||
** this avoids undefined behavior when, for example,
|
||||
** INTMAX_MAX is converted to a larger time_t value before it
|
||||
** is compared.
|
||||
**
|
||||
** On all platforms that we know of, if a time value is
|
||||
** outside intmax_t/uintmax_t range, then it is an integer so we can
|
||||
** simply return it. There's no simple, portable way to check this.
|
||||
** If you know of a counterexample platform, please report a bug.
|
||||
*/
|
||||
if (!TYPE_INTEGRAL(time_t)) {
|
||||
if (INTMAX_MIN < t && t < INTMAX_MAX) {
|
||||
intmax_t i = t;
|
||||
return i;
|
||||
}
|
||||
if (0 <= t && t < UINTMAX_MAX) {
|
||||
uintmax_t i = t;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
** The easy way to behave "as if no library function calls" localtime
|
||||
** is to not call it--so we drop its guts into "localsub", which can be
|
||||
@@ -1283,21 +1315,15 @@ localsub(const time_t *const timep, const int_fast32_t offset,
|
||||
(sp->goahead && t > sp->ats[sp->timecnt - 1])) {
|
||||
time_t newt = t;
|
||||
register time_t seconds;
|
||||
register time_t tcycles;
|
||||
register int_fast64_t icycles;
|
||||
register time_t years;
|
||||
|
||||
if (t < sp->ats[0])
|
||||
seconds = sp->ats[0] - t;
|
||||
else seconds = t - sp->ats[sp->timecnt - 1];
|
||||
--seconds;
|
||||
tcycles = seconds / YEARSPERREPEAT / AVGSECSPERYEAR;
|
||||
++tcycles;
|
||||
icycles = tcycles;
|
||||
if (tcycles - icycles >= 1 || icycles - tcycles >= 1)
|
||||
return NULL;
|
||||
seconds = icycles;
|
||||
seconds *= YEARSPERREPEAT;
|
||||
seconds *= AVGSECSPERYEAR;
|
||||
years = (truncate_time (seconds / SECSPERREPEAT + 1)
|
||||
* YEARSPERREPEAT);
|
||||
seconds = years * AVGSECSPERYEAR;
|
||||
if (t < sp->ats[0])
|
||||
newt += seconds;
|
||||
else newt -= seconds;
|
||||
@@ -1310,8 +1336,8 @@ localsub(const time_t *const timep, const int_fast32_t offset,
|
||||
|
||||
newy = tmp->tm_year;
|
||||
if (t < sp->ats[0])
|
||||
newy -= icycles * YEARSPERREPEAT;
|
||||
else newy += icycles * YEARSPERREPEAT;
|
||||
newy -= years;
|
||||
else newy += years;
|
||||
tmp->tm_year = newy;
|
||||
if (tmp->tm_year != newy)
|
||||
return NULL;
|
||||
|
||||
@@ -167,9 +167,18 @@ typedef int int_fast32_t;
|
||||
# if defined LLONG_MAX || defined __LONG_LONG_MAX__
|
||||
typedef long long intmax_t;
|
||||
# define PRIdMAX "lld"
|
||||
# ifdef LLONG_MAX
|
||||
# define INTMAX_MAX LLONG_MAX
|
||||
# define INTMAX_MIN LLONG_MIN
|
||||
# else
|
||||
# define INTMAX_MAX __LONG_LONG_MAX__
|
||||
# define INTMAX_MIN __LONG_LONG_MIN__
|
||||
# endif
|
||||
# else
|
||||
typedef long intmax_t;
|
||||
# define PRIdMAX "ld"
|
||||
# define INTMAX_MAX LONG_MAX
|
||||
# define INTMAX_MIN LONG_MIN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
@@ -177,9 +186,15 @@ typedef long intmax_t;
|
||||
# if defined ULLONG_MAX || defined __LONG_LONG_MAX__
|
||||
typedef unsigned long long uintmax_t;
|
||||
# define PRIuMAX "llu"
|
||||
# ifdef ULLONG_MAX
|
||||
# define UINTMAX_MAX ULLONG_MAX
|
||||
# else
|
||||
# define UINTMAX_MAX (__LONG_LONG_MAX__ * 2ULL + 1)
|
||||
# endif
|
||||
# else
|
||||
typedef unsigned long uintmax_t;
|
||||
# define PRIuMAX "lu"
|
||||
# define UINTMAX_MAX ULONG_MAX
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user