summaryrefslogtreecommitdiff
path: root/localtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'localtime.c')
-rw-r--r--localtime.c135
1 files changed, 87 insertions, 48 deletions
diff --git a/localtime.c b/localtime.c
index 6a687d7..913c7e8 100644
--- a/localtime.c
+++ b/localtime.c
@@ -105,6 +105,15 @@ struct lsinfo { /* leap second information */
#define SMALLEST(a, b) (((a) < (b)) ? (a) : (b))
#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b))
+/* This abbreviation means local time is unspecified. */
+static char const UNSPEC[] = "-00";
+
+/* How many extra bytes are needed at the end of struct state's chars array.
+ This needs to be at least 1 for null termination in case the input
+ data isn't properly terminated, and it also needs to be big enough
+ for ttunspecified to work without crashing. */
+enum { CHARS_EXTRA = BIGGEST(sizeof UNSPEC, 2) - 1 };
+
#ifdef TZNAME_MAX
#define MY_TZNAME_MAX TZNAME_MAX
#endif /* defined TZNAME_MAX */
@@ -122,7 +131,8 @@ struct state {
time_t ats[TZ_MAX_TIMES];
unsigned char types[TZ_MAX_TIMES];
struct ttinfo ttis[TZ_MAX_TYPES];
- char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+ char chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + CHARS_EXTRA,
+ sizeof gmt),
(2 * (MY_TZNAME_MAX + 1)))];
struct lsinfo lsis[TZ_MAX_LEAPS];
@@ -211,6 +221,15 @@ init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
s->tt_ttisut = false;
}
+/* Return true if SP's time type I does not specify local time. */
+static bool
+ttunspecified(struct state const *sp, int i)
+{
+ char const *abbr = &sp->chars[sp->ttis[i].tt_desigidx];
+ /* memcmp is likely faster than strcmp, and is safe due to CHARS_EXTRA. */
+ return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0;
+}
+
static int_fast32_t
detzcode(const char *const codep)
{
@@ -434,35 +453,45 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
if (close(fid) < 0)
return errno;
for (stored = 4; stored <= 8; stored *= 2) {
- int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
- int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
- int_fast64_t prevtr = -1;
- int_fast32_t prevcorr;
- int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
- int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
- int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
- int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
- char const *p = up->buf + tzheadsize;
- /* Although tzfile(5) currently requires typecnt to be nonzero,
- support future formats that may allow zero typecnt
- in files that have a TZ string and no transitions. */
- if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
- && 0 <= typecnt && typecnt < TZ_MAX_TYPES
- && 0 <= timecnt && timecnt < TZ_MAX_TIMES
- && 0 <= charcnt && charcnt < TZ_MAX_CHARS
- && (ttisstdcnt == typecnt || ttisstdcnt == 0)
- && (ttisutcnt == typecnt || ttisutcnt == 0)))
- return EINVAL;
- if (nread
- < (tzheadsize /* struct tzhead */
- + timecnt * stored /* ats */
+ char version = up->tzhead.tzh_version[0];
+ bool skip_datablock = stored == 4 && version;
+ int_fast32_t datablock_size;
+ int_fast32_t ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt);
+ int_fast32_t ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt);
+ int_fast64_t prevtr = -1;
+ int_fast32_t prevcorr;
+ int_fast32_t leapcnt = detzcode(up->tzhead.tzh_leapcnt);
+ int_fast32_t timecnt = detzcode(up->tzhead.tzh_timecnt);
+ int_fast32_t typecnt = detzcode(up->tzhead.tzh_typecnt);
+ int_fast32_t charcnt = detzcode(up->tzhead.tzh_charcnt);
+ char const *p = up->buf + tzheadsize;
+ /* Although tzfile(5) currently requires typecnt to be nonzero,
+ support future formats that may allow zero typecnt
+ in files that have a TZ string and no transitions. */
+ if (! (0 <= leapcnt && leapcnt < TZ_MAX_LEAPS
+ && 0 <= typecnt && typecnt < TZ_MAX_TYPES
+ && 0 <= timecnt && timecnt < TZ_MAX_TIMES
+ && 0 <= charcnt && charcnt < TZ_MAX_CHARS
+ && 0 <= ttisstdcnt && ttisstdcnt < TZ_MAX_TYPES
+ && 0 <= ttisutcnt && ttisutcnt < TZ_MAX_TYPES))
+ return EINVAL;
+ datablock_size
+ = (timecnt * stored /* ats */
+ timecnt /* types */
+ typecnt * 6 /* ttinfos */
+ charcnt /* chars */
+ leapcnt * (stored + 4) /* lsinfos */
+ ttisstdcnt /* ttisstds */
- + ttisutcnt)) /* ttisuts */
+ + ttisutcnt); /* ttisuts */
+ if (nread < tzheadsize + datablock_size)
+ return EINVAL;
+ if (skip_datablock)
+ p += datablock_size;
+ else {
+ if (! ((ttisstdcnt == typecnt || ttisstdcnt == 0)
+ && (ttisutcnt == typecnt || ttisutcnt == 0)))
return EINVAL;
+
sp->leapcnt = leapcnt;
sp->timecnt = timecnt;
sp->typecnt = typecnt;
@@ -518,7 +547,9 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
}
for (i = 0; i < sp->charcnt; ++i)
sp->chars[i] = *p++;
- sp->chars[i] = '\0'; /* ensure '\0' at end */
+ /* Ensure '\0'-terminated, and make it safe to call
+ ttunspecified later. */
+ memset(&sp->chars[i], 0, CHARS_EXTRA);
/* Read leap seconds, discarding those out of time_t range. */
leapcnt = 0;
@@ -526,24 +557,29 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p);
int_fast32_t corr = detzcode(p + stored);
p += stored + 4;
+
/* Leap seconds cannot occur before the Epoch,
or out of order. */
if (tr <= prevtr)
return EINVAL;
+
+ /* To avoid other botches in this code, each leap second's
+ correction must differ from the previous one's by 1
+ second or less, except that the first correction can be
+ any value; these requirements are more generous than
+ RFC 8536, to allow future RFC extensions. */
+ if (! (i == 0
+ || (prevcorr < corr
+ ? corr == prevcorr + 1
+ : (corr == prevcorr
+ || corr == prevcorr - 1))))
+ return EINVAL;
+ prevtr = tr;
+ prevcorr = corr;
+
if (tr <= TIME_T_MAX) {
- /* To avoid other botches in this code, each leap second's
- correction must differ from the previous one's by 1
- second or less, except that the first correction can be
- any value; these requirements are more generous than
- RFC 8536, to allow future RFC extensions. */
- if (! (i == 0
- || (prevcorr < corr
- ? corr == prevcorr + 1
- : (corr == prevcorr
- || corr == prevcorr - 1))))
- return EINVAL;
- sp->lsis[leapcnt].ls_trans = prevtr = tr;
- sp->lsis[leapcnt].ls_corr = prevcorr = corr;
+ sp->lsis[leapcnt].ls_trans = tr;
+ sp->lsis[leapcnt].ls_corr = corr;
leapcnt++;
}
}
@@ -573,13 +609,14 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
ttisp->tt_ttisut = *p++;
}
}
- /*
- ** If this is an old file, we're done.
- */
- if (up->tzhead.tzh_version[0] == '\0')
- break;
- nread -= p - up->buf;
- memmove(up->buf, p, nread);
+ }
+
+ nread -= p - up->buf;
+ memmove(up->buf, p, nread);
+
+ /* If this is an old file, we're done. */
+ if (!version)
+ break;
}
if (doextend && nread > 2 &&
up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
@@ -682,13 +719,13 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
standard-time type. See:
https://mm.icann.org/pipermail/tz/2013-May/019368.html */
/*
- ** If type 0 is unused in transitions,
+ ** If type 0 does not specify local time, or is unused in transitions,
** it's the type to use for early times.
*/
for (i = 0; i < sp->timecnt; ++i)
if (sp->types[i] == 0)
break;
- i = i < sp->timecnt ? -1 : 0;
+ i = i < sp->timecnt && ! ttunspecified(sp, 0) ? -1 : 0;
/*
** Absent the above,
** if there are transition times
@@ -2073,6 +2110,8 @@ time2sub(struct tm *const tmp,
for (j = sp->typecnt - 1; j >= 0; --j) {
if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
continue;
+ if (ttunspecified(sp, j))
+ continue;
newt = (t + sp->ttis[j].tt_utoff
- sp->ttis[i].tt_utoff);
if (! funcp(sp, &newt, offset, &mytm))
@@ -2165,7 +2204,7 @@ time1(struct tm *const tmp,
seen[i] = false;
nseen = 0;
for (i = sp->timecnt - 1; i >= 0; --i)
- if (!seen[sp->types[i]]) {
+ if (!seen[sp->types[i]] && !ttunspecified(sp, sp->types[i])) {
seen[sp->types[i]] = true;
types[nseen++] = sp->types[i];
}