* localtime.c (localsub): Redo computation of NEWT to avoid integer overflow when SECONDS is close to the maximum time_t value. Without this fix, localtime mishandles TZ="EST5EDT4,0/0,J365/0" by incorrectly omitting transitions before 1970. For example, "zdump -i 'EST5EDT4,0/0,J365/0'" incorrectly lists 1970-01-01 as the first transition date. --- NEWS | 4 ++++ localtime.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 8e968eb..af5dd9f 100644 --- a/NEWS +++ b/NEWS @@ -12,6 +12,10 @@ Unreleased, experimental changes set to a all-year DST string like "EST5EDT4,0/0,J365/25" that does not conform to POSIX but does conform to Internet RFC 8536. + Fix another bug that caused 'localtime' etc. to crash when TZ was + set to a POSIX-conforming but unusual TZ string like + "EST5EDT4,0/0,J365/0", where almost all the year is DST. + Fix bug in zic -r; in some cases, the dummy time type after the last time transition disagreed with the TZ string, contrary to Internet RFC 8563 section 3.3. diff --git a/localtime.c b/localtime.c index 333c6ea..b40e5e8 100644 --- a/localtime.c +++ b/localtime.c @@ -1459,7 +1459,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, } if ((sp->goback && t < sp->ats[0]) || (sp->goahead && t > sp->ats[sp->timecnt - 1])) { - time_t newt = t; + time_t newt; register time_t seconds; register time_t years; @@ -1467,11 +1467,17 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, seconds = sp->ats[0] - t; else seconds = t - sp->ats[sp->timecnt - 1]; --seconds; - years = (seconds / SECSPERREPEAT + 1) * YEARSPERREPEAT; + + /* Beware integer overflow, as SECONDS might + be close to the maximum time_t. */ + years = seconds / SECSPERREPEAT * YEARSPERREPEAT; seconds = years * AVGSECSPERYEAR; + years += YEARSPERREPEAT; if (t < sp->ats[0]) - newt += seconds; - else newt -= seconds; + newt = t + seconds + SECSPERREPEAT; + else + newt = t - seconds - SECSPERREPEAT; + if (newt < sp->ats[0] || newt > sp->ats[sp->timecnt - 1]) return NULL; /* "cannot happen" */ -- 2.27.0