[tz] [PATCH 4/4] Fix integer overflow with TZ="EST5EDT4, 0/0, J365/0"
Paul Eggert
eggert at cs.ucla.edu
Thu Feb 18 03:29:44 UTC 2021
* 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
More information about the tz
mailing list