diff options
Diffstat (limited to 'localtime.c')
| -rw-r--r-- | localtime.c | 744 |
1 files changed, 396 insertions, 348 deletions
diff --git a/localtime.c b/localtime.c index b80a34138370..0091b81eba3d 100644 --- a/localtime.c +++ b/localtime.c @@ -52,6 +52,10 @@ struct stat { char st_ctime, st_dev, st_ino; }; # define THREAD_TM_MULTI 0 #endif +#ifndef USE_TIMEX_T +# define USE_TIMEX_T false +#endif + #if THREAD_SAFE # include <pthread.h> @@ -88,6 +92,10 @@ extern int __isthreaded; # endif # endif # endif +#endif + +#if !defined TM_GMTOFF || !USE_TIMEX_T +# if THREAD_SAFE /* True if the current process might be multi-threaded, false if it is definitely single-threaded. @@ -98,24 +106,25 @@ extern int __isthreaded; static bool is_threaded(void) { -# if THREAD_PREFER_SINGLE && HAVE___ISTHREADED +# if THREAD_PREFER_SINGLE && HAVE___ISTHREADED return !!__isthreaded; -# elif THREAD_PREFER_SINGLE && HAVE_SYS_SINGLE_THREADED_H +# elif THREAD_PREFER_SINGLE && HAVE_SYS_SINGLE_THREADED_H return !__libc_single_threaded; -# else +# else return true; -# endif +# endif } -# if THREAD_RWLOCK +# if THREAD_RWLOCK static pthread_rwlock_t locallock = PTHREAD_RWLOCK_INITIALIZER; static int dolock(void) { return pthread_rwlock_rdlock(&locallock); } static void dounlock(void) { pthread_rwlock_unlock(&locallock); } -# else +# else static pthread_mutex_t locallock = PTHREAD_MUTEX_INITIALIZER; static int dolock(void) { return pthread_mutex_lock(&locallock); } static void dounlock(void) { pthread_mutex_unlock(&locallock); } -# endif +# endif + /* Get a lock. Return 0 on success, a positive errno value on failure, negative if known to be single-threaded so no lock is needed. */ static int @@ -131,25 +140,12 @@ unlock(bool threaded) if (threaded) dounlock(); } -#else +# else static int lock(void) { return -1; } static void unlock(ATTRIBUTE_MAYBE_UNUSED bool threaded) { } +# endif #endif -/* If THREADED, upgrade a read lock to a write lock. - Return 0 on success, a positive errno value otherwise. */ -static int -rd2wrlock(ATTRIBUTE_MAYBE_UNUSED bool threaded) -{ -#if THREAD_RWLOCK - if (threaded) { - dounlock(); - return pthread_rwlock_wrlock(&locallock); - } -#endif - return 0; -} - #if THREAD_SAFE typedef pthread_once_t once_t; # define ONCE_INIT PTHREAD_ONCE_INIT @@ -187,36 +183,6 @@ tm_multi_key_init(void) #endif -/* Return TMP, or a thread-specific struct tm * selected by WHICH. */ -static struct tm * -tm_multi(struct tm *tmp, ATTRIBUTE_MAYBE_UNUSED enum tm_multi which) -{ -#if THREAD_SAFE && THREAD_TM_MULTI - /* It is OK to check is_threaded() separately here; even if it - returns a different value in other places in the caller, - this function's behavior is still valid. */ - if (is_threaded()) { - /* Try to get a thread-specific struct tm *. - Fall back on TMP if this fails. */ - static pthread_once_t tm_multi_once = PTHREAD_ONCE_INIT; - pthread_once(&tm_multi_once, tm_multi_key_init); - if (!tm_multi_key_err) { - struct tm *p = pthread_getspecific(tm_multi_key); - if (!p) { - p = malloc(N_TM_MULTI * sizeof *p); - if (p && pthread_setspecific(tm_multi_key, p) != 0) { - free(p); - p = NULL; - } - } - if (p) - return &p[which]; - } - } -#endif - return tmp; -} - /* Unless intptr_t is missing, pacify gcc -Wcast-qual on char const * exprs. Use this carefully, as the casts disable type checking. This is a macro so that it can be used in static initializers. */ @@ -293,9 +259,6 @@ typedef time_t monotime_t; to a static function that returns the redefined time_t. It also tells us to define only data and code needed to support the offtime or mktime variant. */ -#ifndef USE_TIMEX_T -# define USE_TIMEX_T false -#endif #if USE_TIMEX_T # undef TIME_T_MIN # undef TIME_T_MAX @@ -446,7 +409,7 @@ static char const *utc = etc_utc + sizeof "Etc/" - 1; #endif /* -** The DST rules to use if TZ has no rules and we can't load TZDEFRULES. +** The DST rules to use if TZ has no rules. ** Default to US rules as of 2017-05-07. ** POSIX does not specify the default DST rules; ** for historical reasons, US rules are a common default. @@ -496,8 +459,19 @@ typedef ptrdiff_t desigidx_type; # error "TZNAME_MAXIMUM too large" #endif +/* A type that can represent any 32-bit two's complement integer, + i.e., any integer in the range -2**31 .. 2**31 - 1. + Ordinarily this is int_fast32_t, but on non-C23 hosts + that are not two's complement it is int_fast64_t. */ +#if INT_FAST32_MIN < -TWO_31_MINUS_1 +typedef int_fast32_t int_fast32_2s; +#else +typedef int_fast64_t int_fast32_2s; +#endif + struct ttinfo { /* time type information */ - int_least32_t tt_utoff; /* UT offset in seconds */ + int_least32_t tt_utoff; /* UT offset in seconds; in the range + -2**31 + 1 .. 2**31 - 1 */ desigidx_type tt_desigidx; /* abbreviation list index */ bool tt_isdst; /* used to set tm_isdst */ bool tt_ttisstd; /* transition is std time */ @@ -505,8 +479,8 @@ struct ttinfo { /* time type information */ }; struct lsinfo { /* leap second information */ - time_t ls_trans; /* transition time */ - int_fast32_t ls_corr; /* correction to apply */ + time_t ls_trans; /* transition time (positive) */ + int_fast32_2s ls_corr; /* correction to apply */ }; /* This abbreviation means local time is unspecified. */ @@ -524,7 +498,9 @@ enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 }; are put on the stack and stacks are relatively small on some platforms. See tzfile.h for more about the sizes. */ struct state { +#if TZ_RUNTIME_LEAPS int leapcnt; +#endif int timecnt; int typecnt; int charcnt; @@ -535,9 +511,48 @@ struct state { struct ttinfo ttis[TZ_MAX_TYPES]; char chars[max(max(TZ_MAX_CHARS + CHARS_EXTRA, sizeof "UTC"), 2 * (TZNAME_MAXIMUM + 1))]; +#if TZ_RUNTIME_LEAPS struct lsinfo lsis[TZ_MAX_LEAPS]; +#endif }; +static int +leapcount(ATTRIBUTE_MAYBE_UNUSED struct state const *sp) +{ +#if TZ_RUNTIME_LEAPS + return sp->leapcnt; +#else + return 0; +#endif +} +static void +set_leapcount(ATTRIBUTE_MAYBE_UNUSED struct state *sp, + ATTRIBUTE_MAYBE_UNUSED int leapcnt) +{ +#if TZ_RUNTIME_LEAPS + sp->leapcnt = leapcnt; +#endif +} +static struct lsinfo +lsinfo(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, + ATTRIBUTE_MAYBE_UNUSED int i) +{ +#if TZ_RUNTIME_LEAPS + return sp->lsis[i]; +#else + unreachable(); +#endif +} +static void +set_lsinfo(ATTRIBUTE_MAYBE_UNUSED struct state *sp, + ATTRIBUTE_MAYBE_UNUSED int i, + ATTRIBUTE_MAYBE_UNUSED struct lsinfo lsinfo) +{ +#if TZ_RUNTIME_LEAPS + sp->lsis[i] = lsinfo; +#endif +} + enum r_type { JULIAN_DAY, /* Jn = Julian day */ DAY_OF_YEAR, /* n = day of year */ @@ -555,8 +570,8 @@ struct rule { static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t, struct tm *); static bool increment_overflow(int *, int); -static bool increment_overflow_time(time_t *, int_fast32_t); -static int_fast32_t leapcorr(struct state const *, time_t); +static bool increment_overflow_time(time_t *, int_fast32_2s); +static int_fast32_2s leapcorr(struct state const *, time_t); static struct tm *timesub(time_t const *, int_fast32_t, struct state const *, struct tm *); static bool tzparse(char const *, struct state *, struct state const *); @@ -638,15 +653,14 @@ ttunspecified(struct state const *sp, int i) return memcmp(abbr, UNSPEC, sizeof UNSPEC) == 0; } -static int_fast32_t +static int_fast32_2s detzcode(const char *const codep) { - register int_fast32_t result; register int i; - int_fast32_t one = 1; - int_fast32_t halfmaxval = one << (32 - 2); - int_fast32_t maxval = halfmaxval - 1 + halfmaxval; - int_fast32_t minval = -1 - maxval; + int_fast32_2s + maxval = TWO_31_MINUS_1, + minval = -1 - maxval, + result; result = codep[0] & 0x7f; for (i = 1; i < 4; ++i) @@ -654,8 +668,7 @@ detzcode(const char *const codep) if (codep[0] & 0x80) { /* Do two's-complement negation even on non-two's-complement machines. - If the result would be minval - 1, return minval. */ - result -= !TWOS_COMPLEMENT(int_fast32_t) && result != 0; + This cannot overflow, as int_fast32_2s is wide enough. */ result += minval; } return result; @@ -1033,24 +1046,24 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, 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); + int_fast32_2s + ttisstdcnt = detzcode(up->tzhead.tzh_ttisstdcnt), + ttisutcnt = detzcode(up->tzhead.tzh_ttisutcnt), + leapcnt = detzcode(up->tzhead.tzh_leapcnt), + timecnt = detzcode(up->tzhead.tzh_timecnt), + typecnt = detzcode(up->tzhead.tzh_typecnt), + 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)) + if (! (0 <= leapcnt + && leapcnt <= (TZ_RUNTIME_LEAPS ? TZ_MAX_LEAPS : 0) + && 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 */ @@ -1064,12 +1077,13 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, return EINVAL; if (skip_datablock) p += datablock_size; + else if (! ((ttisstdcnt == typecnt || ttisstdcnt == 0) + && (ttisutcnt == typecnt || ttisutcnt == 0))) + return EINVAL; else { - if (! ((ttisstdcnt == typecnt || ttisstdcnt == 0) - && (ttisutcnt == typecnt || ttisutcnt == 0))) - return EINVAL; - - sp->leapcnt = leapcnt; + int_fast64_t prevtr = -1; + int_fast32_2s prevcorr; + set_leapcount(sp, leapcnt); sp->timecnt = timecnt; sp->typecnt = typecnt; sp->charcnt = charcnt; @@ -1109,9 +1123,16 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, for (i = 0; i < sp->typecnt; ++i) { register struct ttinfo * ttisp; unsigned char isdst, desigidx; + int_fast32_2s utoff = detzcode(p); + + /* Reject a UT offset equal to -2**31, as it might + cause trouble both in this file and in callers. + Also, it violates RFC 9636 section 3.2. */ + if (utoff < -TWO_31_MINUS_1) + return EINVAL; ttisp = &sp->ttis[i]; - ttisp->tt_utoff = detzcode(p); + ttisp->tt_utoff = utoff; p += 4; isdst = *p++; if (! (isdst < 2)) @@ -1130,9 +1151,9 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, /* Read leap seconds, discarding those out of time_t range. */ leapcnt = 0; - for (i = 0; i < sp->leapcnt; ++i) { + for (i = 0; i < leapcount(sp); i++) { int_fast64_t tr = stored == 4 ? detzcode(p) : detzcode64(p); - int_fast32_t corr = detzcode(p + stored); + int_fast32_2s corr = detzcode(p + stored); p += stored + 4; /* Leap seconds cannot occur before the Epoch, @@ -1155,12 +1176,14 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, prevcorr = corr; if (tr <= TIME_T_MAX) { - sp->lsis[leapcnt].ls_trans = tr; - sp->lsis[leapcnt].ls_corr = corr; + struct lsinfo ls; + ls.ls_trans = tr; + ls.ls_corr = corr; + set_lsinfo(sp, leapcnt, ls); leapcnt++; } } - sp->leapcnt = leapcnt; + set_leapcount(sp, leapcnt); for (i = 0; i < sp->typecnt; ++i) { register struct ttinfo * ttisp; @@ -1204,8 +1227,8 @@ tzloadbody(char const *name, struct state *sp, char tzloadflags, if (tzparse(&up->buf[1], ts, sp)) { /* Attempt to reuse existing abbreviations. - Without this, America/Anchorage would be right on - the edge after 2037 when TZ_MAX_CHARS is 50, as + Without this, America/Anchorage would + consume 50 bytes for abbreviations, as sp->charcnt equals 40 (for LMT AST AWT APT AHST AHDT YST AKDT AKST) and ts->charcnt equals 10 (for AKST AKDT). Reusing means sp->charcnt can @@ -1600,7 +1623,6 @@ tzparse(const char *name, struct state *sp, struct state const *basep) int_fast32_t stdoffset; int_fast32_t dstoffset; register char * cp; - register bool load_ok; ptrdiff_t stdlen, dstlen, charcnt; time_t atlo = TIME_T_MIN, leaplo = TIME_T_MIN; @@ -1626,18 +1648,22 @@ tzparse(const char *name, struct state *sp, struct state const *basep) if (basep) { if (0 < basep->timecnt) atlo = basep->ats[basep->timecnt - 1]; - load_ok = false; - sp->leapcnt = basep->leapcnt; - memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis); - } else { - load_ok = tzload(TZDEFRULES, sp, 0) == 0; - if (!load_ok) - sp->leapcnt = 0; /* So, we're off a little. */ - } - if (0 < sp->leapcnt) - leaplo = sp->lsis[sp->leapcnt - 1].ls_trans; + set_leapcount(sp, leapcount(basep)); + if (0 < leapcount(sp)) { + int i; + for (i = 0; i < leapcount(sp); i++) + set_lsinfo(sp, i, lsinfo(basep, i)); + leaplo = lsinfo(sp, leapcount(sp) - 1).ls_trans; + } + } else + set_leapcount(sp, 0); /* So, we're off a little. */ sp->goback = sp->goahead = false; if (*name != '\0') { + struct rule start, end; + int year, yearbeg, yearlim, timecnt; + time_t janfirst; + int_fast32_t janoffset = 0; + if (*name == '<') { dstname = ++name; name = getqzname(name, '>'); @@ -1658,194 +1684,102 @@ tzparse(const char *name, struct state *sp, struct state const *basep) if (name == NULL) return false; } else dstoffset = stdoffset - SECSPERHOUR; - if (*name == '\0' && !load_ok) - name = TZDEFRULESTRING; - if (*name == ',' || *name == ';') { - struct rule start; - struct rule end; - register int year; - register int timecnt; - time_t janfirst; - int_fast32_t janoffset = 0; - int yearbeg, yearlim; - ++name; - if ((name = getrule(name, &start)) == NULL) - return false; - if (*name++ != ',') - return false; - if ((name = getrule(name, &end)) == NULL) - return false; - if (*name != '\0') - return false; - sp->typecnt = 2; /* standard time and DST */ - /* - ** Two transitions per year, from EPOCH_YEAR forward. - */ - init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); - init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); - timecnt = 0; - janfirst = 0; - yearbeg = EPOCH_YEAR; + if (*name == '\0') + name = TZDEFRULESTRING; + if (! (*name == ',' || *name == ';')) + return false; - do { - int_fast32_t yearsecs - = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; - time_t janfirst1 = janfirst; - yearbeg--; - if (increment_overflow_time(&janfirst1, -yearsecs)) { - janoffset = -yearsecs; - break; - } - janfirst = janfirst1; - } while (atlo < janfirst - && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); + name = getrule(name + 1, &start); + if (!name) + return false; + if (*name++ != ',') + return false; + name = getrule(name, &end); + if (!name || *name) + return false; + sp->typecnt = 2; /* standard time and DST */ + /* + ** Two transitions per year, from EPOCH_YEAR forward. + */ + init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); + init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); + timecnt = 0; + janfirst = 0; + yearbeg = EPOCH_YEAR; - while (true) { - int_fast32_t yearsecs - = year_lengths[isleap(yearbeg)] * SECSPERDAY; - int yearbeg1 = yearbeg; - time_t janfirst1 = janfirst; - if (increment_overflow_time(&janfirst1, yearsecs) - || increment_overflow(&yearbeg1, 1) - || atlo <= janfirst1) - break; - yearbeg = yearbeg1; - janfirst = janfirst1; - } + do { + int_fast32_t yearsecs + = year_lengths[isleap(yearbeg - 1)] * SECSPERDAY; + time_t janfirst1 = janfirst; + yearbeg--; + if (increment_overflow_time(&janfirst1, -yearsecs)) { + janoffset = -yearsecs; + break; + } + janfirst = janfirst1; + } while (atlo < janfirst + && EPOCH_YEAR - YEARSPERREPEAT / 2 < yearbeg); - yearlim = yearbeg; - if (increment_overflow(&yearlim, years_of_observations)) - yearlim = INT_MAX; - for (year = yearbeg; year < yearlim; year++) { - int_fast32_t - starttime = transtime(year, &start, stdoffset), - endtime = transtime(year, &end, dstoffset); - int_fast32_t - yearsecs = (year_lengths[isleap(year)] - * SECSPERDAY); - bool reversed = endtime < starttime; - if (reversed) { - int_fast32_t swap = starttime; - starttime = endtime; - endtime = swap; - } - if (reversed - || (starttime < endtime - && endtime - starttime < yearsecs)) { - if (TZ_MAX_TIMES - 2 < timecnt) - break; - sp->ats[timecnt] = janfirst; - if (! increment_overflow_time - (&sp->ats[timecnt], - janoffset + starttime) - && atlo <= sp->ats[timecnt]) - sp->types[timecnt++] = !reversed; - sp->ats[timecnt] = janfirst; - if (! increment_overflow_time - (&sp->ats[timecnt], - janoffset + endtime) - && atlo <= sp->ats[timecnt]) { - sp->types[timecnt++] = reversed; - } - } - if (endtime < leaplo) { - yearlim = year; - if (increment_overflow(&yearlim, - years_of_observations)) - yearlim = INT_MAX; - } - if (increment_overflow_time - (&janfirst, janoffset + yearsecs)) - break; - janoffset = 0; - } - sp->timecnt = timecnt; - if (! timecnt) { - sp->ttis[0] = sp->ttis[1]; - sp->typecnt = 1; /* Perpetual DST. */ - } else if (years_of_observations <= year - yearbeg) - sp->goback = sp->goahead = true; - } else { - register int_fast32_t theirstdoffset; - register int_fast32_t theirdstoffset; - register int_fast32_t theiroffset; - register bool isdst; - register int i; - register int j; + while (true) { + int_fast32_t yearsecs + = year_lengths[isleap(yearbeg)] * SECSPERDAY; + int yearbeg1 = yearbeg; + time_t janfirst1 = janfirst; + if (increment_overflow_time(&janfirst1, yearsecs) + || increment_overflow(&yearbeg1, 1) + || atlo <= janfirst1) + break; + yearbeg = yearbeg1; + janfirst = janfirst1; + } - if (*name != '\0') - return false; - /* - ** Initial values of theirstdoffset and theirdstoffset. - */ - theirstdoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (!sp->ttis[j].tt_isdst) { - theirstdoffset = - - sp->ttis[j].tt_utoff; - break; - } - } - theirdstoffset = 0; - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - if (sp->ttis[j].tt_isdst) { - theirdstoffset = - - sp->ttis[j].tt_utoff; - break; - } - } - /* - ** Initially we're assumed to be in standard time. - */ - isdst = false; - /* - ** Now juggle transition times and types - ** tracking offsets as you do. - */ - for (i = 0; i < sp->timecnt; ++i) { - j = sp->types[i]; - sp->types[i] = sp->ttis[j].tt_isdst; - if (sp->ttis[j].tt_ttisut) { - /* No adjustment to transition time */ - } else { - /* - ** If daylight saving time is in - ** effect, and the transition time was - ** not specified as standard time, add - ** the daylight saving time offset to - ** the transition time; otherwise, add - ** the standard time offset to the - ** transition time. - */ - /* - ** Transitions from DST to DDST - ** will effectively disappear since - ** proleptic TZ strings have only one - ** DST offset. - */ - if (isdst && !sp->ttis[j].tt_ttisstd) { - sp->ats[i] += dstoffset - - theirdstoffset; - } else { - sp->ats[i] += stdoffset - - theirstdoffset; - } - } - theiroffset = -sp->ttis[j].tt_utoff; - if (sp->ttis[j].tt_isdst) - theirdstoffset = theiroffset; - else theirstdoffset = theiroffset; - } - /* - ** Finally, fill in ttis. - */ - init_ttinfo(&sp->ttis[0], -stdoffset, false, 0); - init_ttinfo(&sp->ttis[1], -dstoffset, true, stdlen + 1); - sp->typecnt = 2; + yearlim = yearbeg; + if (increment_overflow(&yearlim, years_of_observations)) + yearlim = INT_MAX; + for (year = yearbeg; year < yearlim; year++) { + int_fast32_t + starttime = transtime(year, &start, stdoffset), + endtime = transtime(year, &end, dstoffset), + yearsecs = year_lengths[isleap(year)] * SECSPERDAY; + bool reversed = endtime < starttime; + if (reversed) { + int_fast32_t swap = starttime; + starttime = endtime; + endtime = swap; + } + if (reversed + || (starttime < endtime + && endtime - starttime < yearsecs)) { + if (TZ_MAX_TIMES - 2 < timecnt) + break; + sp->ats[timecnt] = janfirst; + if (! increment_overflow_time(&sp->ats[timecnt], + janoffset + starttime) + && atlo <= sp->ats[timecnt]) + sp->types[timecnt++] = !reversed; + sp->ats[timecnt] = janfirst; + if (! increment_overflow_time(&sp->ats[timecnt], + janoffset + endtime) + && atlo <= sp->ats[timecnt]) { + sp->types[timecnt++] = reversed; + } + } + if (endtime < leaplo) { + yearlim = year; + if (increment_overflow(&yearlim, years_of_observations)) + yearlim = INT_MAX; + } + if (increment_overflow_time(&janfirst, janoffset + yearsecs)) + break; + janoffset = 0; } + sp->timecnt = timecnt; + if (! timecnt) { + sp->ttis[0] = sp->ttis[1]; + sp->typecnt = 1; /* Perpetual DST. */ + } else if (years_of_observations <= year - yearbeg) + sp->goback = sp->goahead = true; } else { dstlen = 0; sp->typecnt = 1; /* only standard time */ @@ -1866,7 +1800,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep) static void gmtload(struct state *const sp) { - if (tzload(etc_utc, sp, TZLOAD_TZSTRING) != 0) + if (!TZ_RUNTIME_LEAPS || tzload(etc_utc, sp, TZLOAD_TZSTRING) != 0) tzparse("UTC0", sp, NULL); } @@ -1898,7 +1832,7 @@ zoneinit(struct state *sp, char const *name, char tzloadflags) /* ** User wants it fast rather than right. */ - sp->leapcnt = 0; /* so, we're off a little */ + set_leapcount(sp, 0); /* so, we're off a little */ sp->timecnt = 0; sp->typecnt = 0; sp->charcnt = 0; @@ -1917,6 +1851,20 @@ zoneinit(struct state *sp, char const *name, char tzloadflags) } } +/* If THREADED, upgrade a read lock to a write lock. + Return 0 on success, a positive errno value otherwise. */ +static int +rd2wrlock(ATTRIBUTE_MAYBE_UNUSED bool threaded) +{ +# if THREAD_RWLOCK + if (threaded) { + dounlock(); + return pthread_rwlock_wrlock(&locallock); + } +# endif + return 0; +} + /* Like tzset(), but in a critical section. If THREADED && THREAD_RWLOCK the caller has a read lock, and this function might upgrade it to a write lock. @@ -2191,7 +2139,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, ** To get (wrong) behavior that's compatible with System V Release 2.0 ** you'd replace the statement below with ** t += ttisp->tt_utoff; - ** timesub(&t, 0L, sp, tmp); + ** timesub(&t, 0, sp, tmp); */ result = timesub(&t, ttisp->tt_utoff, sp, tmp); if (result) { @@ -2208,6 +2156,36 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, #if !USE_TIMEX_T +/* Return TMP, or a thread-specific struct tm * selected by WHICH. */ +static struct tm * +tm_multi(struct tm *tmp, ATTRIBUTE_MAYBE_UNUSED enum tm_multi which) +{ +# if THREAD_SAFE && THREAD_TM_MULTI + /* It is OK to check is_threaded() separately here; even if it + returns a different value in other places in the caller, + this function's behavior is still valid. */ + if (is_threaded()) { + /* Try to get a thread-specific struct tm *. + Fall back on TMP if this fails. */ + static pthread_once_t tm_multi_once = PTHREAD_ONCE_INIT; + pthread_once(&tm_multi_once, tm_multi_key_init); + if (!tm_multi_key_err) { + struct tm *p = pthread_getspecific(tm_multi_key); + if (!p) { + p = malloc(N_TM_MULTI * sizeof *p); + if (p && pthread_setspecific(tm_multi_key, p) != 0) { + free(p); + p = NULL; + } + } + if (p) + return &p[which]; + } + } +# endif + return tmp; +} + # if NETBSD_INSPIRED struct tm * localtime_rz(struct state *restrict sp, time_t const *restrict timep, @@ -2341,10 +2319,9 @@ static struct tm * timesub(const time_t *timep, int_fast32_t offset, const struct state *sp, struct tm *tmp) { - register const struct lsinfo * lp; register time_t tdays; register const int * ip; - register int_fast32_t corr; + int_fast32_2s corr; register int i; int_fast32_t idays, rem, dayoff, dayrem; time_t y; @@ -2355,13 +2332,13 @@ timesub(const time_t *timep, int_fast32_t offset, time_t secs_since_posleap = SECSPERMIN; corr = 0; - i = (sp == NULL) ? 0 : sp->leapcnt; + i = sp ? leapcount(sp) : 0; while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) { - corr = lp->ls_corr; - if ((i == 0 ? 0 : lp[-1].ls_corr) < corr) - secs_since_posleap = *timep - lp->ls_trans; + struct lsinfo ls = lsinfo(sp, i); + if (ls.ls_trans <= *timep) { + corr = ls.ls_corr; + if ((i == 0 ? 0 : lsinfo(sp, i - 1).ls_corr) < corr) + secs_since_posleap = *timep - ls.ls_trans; break; } } @@ -2491,6 +2468,19 @@ increment_overflow(int *ip, int j) } static bool +increment_overflow_64(int *ip, int_fast64_t j) +{ +#ifdef ckd_add + return ckd_add(ip, *ip, j); +#else + if (j < 0 ? *ip < INT_MIN - j : INT_MAX - j < *ip) + return true; + *ip += j; + return false; +#endif +} + +static bool increment_overflow_time_iinntt(time_t *tp, iinntt j) { #ifdef ckd_add @@ -2506,7 +2496,22 @@ increment_overflow_time_iinntt(time_t *tp, iinntt j) } static bool -increment_overflow_time(time_t *tp, int_fast32_t j) +increment_overflow_time_64(time_t *tp, int_fast64_t j) +{ +#ifdef ckd_add + return ckd_add(tp, *tp, j); +#else + if (j < 0 + ? (TYPE_SIGNED(time_t) ? *tp < TIME_T_MIN - j : *tp <= -1 - j) + : TIME_T_MAX - j < *tp) + return true; + *tp += j; + return false; +#endif +} + +static bool +increment_overflow_time(time_t *tp, int_fast32_2s j) { #ifdef ckd_add return ckd_add(tp, *tp, j); @@ -2525,6 +2530,15 @@ increment_overflow_time(time_t *tp, int_fast32_t j) #endif } +/* Return A - B, where both are in the range -2**31 + 1 .. 2**31 - 1. + The result cannot overflow. */ +static int_fast64_t +utoff_diff (int_fast32_t a, int_fast32_t b) +{ + int_fast64_t aa = a; + return aa - b; +} + static int tmcomp(register const struct tm *const atmp, register const struct tm *const btmp) @@ -2721,8 +2735,18 @@ time2sub(struct tm *const tmp, It's OK if YOURTM.TM_GMTOFF contains uninitialized data, since the guess gets checked. */ time_t altt = t; - int_fast32_t diff = mytm.TM_GMTOFF - yourtm.TM_GMTOFF; - if (!increment_overflow_time(&altt, diff)) { + int_fast64_t offdiff; + bool v; +# ifdef ckd_sub + v = ckd_sub(&offdiff, mytm.TM_GMTOFF, yourtm.TM_GMTOFF); +# else + /* A ckd_sub approximation that is good enough here. */ + v = !(-TWO_31_MINUS_1 <= yourtm.TM_GMTOFF + && yourtm.TM_GMTOFF <= TWO_31_MINUS_1); + if (!v) + offdiff = utoff_diff(mytm.TM_GMTOFF, yourtm.TM_GMTOFF); +# endif + if (!v && !increment_overflow_time_64(&altt, offdiff)) { struct tm alttm; if (funcp(sp, &altt, offset, &alttm) && alttm.tm_isdst == mytm.tm_isdst @@ -2752,8 +2776,12 @@ time2sub(struct tm *const tmp, continue; if (ttunspecified(sp, j)) continue; - newt = (t + sp->ttis[j].tt_utoff - - sp->ttis[i].tt_utoff); + newt = t; + if (increment_overflow_time_64 + (&newt, + utoff_diff(sp->ttis[j].tt_utoff, + sp->ttis[i].tt_utoff))) + continue; if (! funcp(sp, &newt, offset, &mytm)) continue; if (tmcomp(&mytm, &yourtm) != 0) @@ -2852,17 +2880,20 @@ time1(struct tm *const tmp, continue; for (otherind = 0; otherind < nseen; ++otherind) { otheri = types[otherind]; - if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - tmp->tm_sec += (sp->ttis[otheri].tt_utoff - - sp->ttis[samei].tt_utoff); - tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, sp, offset, &okay); - if (okay) - return t; - tmp->tm_sec -= (sp->ttis[otheri].tt_utoff - - sp->ttis[samei].tt_utoff); - tmp->tm_isdst = !tmp->tm_isdst; + if (sp->ttis[otheri].tt_isdst != tmp->tm_isdst) { + int sec = tmp->tm_sec; + if (!increment_overflow_64 + (&tmp->tm_sec, + utoff_diff(sp->ttis[otheri].tt_utoff, + sp->ttis[samei].tt_utoff))) { + tmp->tm_isdst = !tmp->tm_isdst; + t = time2(tmp, funcp, sp, offset, &okay); + if (okay) + return t; + tmp->tm_isdst = !tmp->tm_isdst; + } + tmp->tm_sec = sec; + } } } return WRONG; @@ -2958,17 +2989,16 @@ timegm(struct tm *tmp) } #endif -static int_fast32_t +static int_fast32_2s leapcorr(struct state const *sp, time_t t) { - register struct lsinfo const * lp; register int i; - i = sp->leapcnt; + i = leapcount(sp); while (--i >= 0) { - lp = &sp->lsis[i]; - if (t >= lp->ls_trans) - return lp->ls_corr; + struct lsinfo ls = lsinfo(sp, i); + if (ls.ls_trans <= t) + return ls.ls_corr; } return 0; } @@ -2980,6 +3010,21 @@ leapcorr(struct state const *sp, time_t t) #if !USE_TIMEX_T # if STD_INSPIRED +static bool +decrement_overflow_time(time_t *tp, int_fast32_2s j) +{ +#ifdef ckd_sub + return ckd_sub(tp, *tp, j); +#else + if (! (j < 0 + ? *tp <= TIME_T_MAX + j + : (TYPE_SIGNED(time_t) ? TIME_T_MIN + j <= *tp : j <= *tp))) + return true; + *tp -= j; + return false; +#endif +} + /* NETBSD_INSPIRED_EXTERN functions are exported to callers if NETBSD_INSPIRED is defined, and are private otherwise. */ # if NETBSD_INSPIRED @@ -2999,7 +3044,13 @@ leapcorr(struct state const *sp, time_t t) NETBSD_INSPIRED_EXTERN time_t time2posix_z(struct state *sp, time_t t) { - return t - leapcorr(sp, t); + if (decrement_overflow_time(&t, leapcorr(sp, t))) { + /* Overflow near maximum time_t value with negative correction. + This can happen with unrealistic-but-valid TZif files. */ + errno = EOVERFLOW; + return -1; + } + return t; } time_t @@ -3022,30 +3073,27 @@ time2posix(time_t t) NETBSD_INSPIRED_EXTERN time_t posix2time_z(struct state *sp, time_t t) { - time_t x; - time_t y; - /* - ** For a positive leap second hit, the result - ** is not unique. For a negative leap second - ** hit, the corresponding time doesn't exist, - ** so we return an adjacent second. - */ - x = t + leapcorr(sp, t); - y = x - leapcorr(sp, x); - if (y < t) { - do { - x++; - y = x - leapcorr(sp, x); - } while (y < t); - x -= y != t; - } else if (y > t) { - do { - --x; - y = x - leapcorr(sp, x); - } while (y > t); - x += y != t; - } - return x; + int i; + for (i = leapcount(sp); 0 <= --i; ) { + struct lsinfo ls = lsinfo(sp, i); + time_t t_corr = t; + + if (increment_overflow_time(&t_corr, ls.ls_corr)) { + if (0 <= ls.ls_corr) { + /* Overflow near maximum time_t value with positive correction. + This can happen with ordinary TZif files with leap seconds. */ + errno = EOVERFLOW; + return -1; + } else { + /* A negative correction overflowed, so keep going. + This can happen with unrealistic-but-valid TZif files. */ + } + } else if (ls.ls_trans <= t_corr) + return (t_corr + - (ls.ls_trans == t_corr + && (i == 0 ? 0 : lsinfo(sp, i - 1).ls_corr) < ls.ls_corr)); + } + return t; } time_t @@ -3087,7 +3135,7 @@ posix2time(time_t t) time_t time(time_t *p) { - time_t r = sys_time(0); + time_t r = sys_time(NULL); if (r != (time_t) -1) { iinntt offset = EPOCH_LOCAL ? timezone : 0; if (offset < IINNTT_MIN + EPOCH_OFFSET |
