Date: Thu, 30 Apr 2026 15:04:12 +0000 From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav <des@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: b72580fcb0a7 - stable/15 - tzcode: Update to 2026b Message-ID: <69f36f6c.3fb61.643fa71c@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch stable/15 has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=b72580fcb0a7647dcc2b18861ac02eb9cfcf5835 commit b72580fcb0a7647dcc2b18861ac02eb9cfcf5835 Author: Dag-Erling Smørgrav <des@FreeBSD.org> AuthorDate: 2026-04-24 20:07:20 +0000 Commit: Dag-Erling Smørgrav <des@FreeBSD.org> CommitDate: 2026-04-30 15:02:03 +0000 tzcode: Update to 2026b MFC after: 1 week (cherry picked from commit 76f642310d55b1d3892c1b1626c427d12f97de3a) --- contrib/tzcode/NEWS | 53 ++++++++++++--- contrib/tzcode/tz-art.html | 2 +- contrib/tzcode/tz-link.html | 22 +++++-- contrib/tzcode/version | 2 +- contrib/tzcode/zic.c | 156 +++++++++++++++++++++++++++++--------------- 5 files changed, 166 insertions(+), 69 deletions(-) diff --git a/contrib/tzcode/NEWS b/contrib/tzcode/NEWS index acd22280cb3a..ea52e67e804a 100644 --- a/contrib/tzcode/NEWS +++ b/contrib/tzcode/NEWS @@ -1,10 +1,47 @@ News for the tz database +Release 2026b - 2026-04-22 23:06:43 -0700 + + Briefly: + British Columbia moved to permanent -07 on 2026-03-09. + Some more overflow bugs have been fixed in zic. + + Changes to future timestamps + + British Columbia’s 2026-03-08 spring forward was its last + foreseeable clock change, as it moved to permanent -07 thereafter. + (Thanks to Arthur David Olson.) Although the change to permanent + -07 legally took place on 2026-03-09, temporarily model the change + to occur on 2026-11-01 at 02:00 instead. This works around a + limitation in CLDR v48.2 (2026-03-17). This temporary hack is + planned to be removed after CLDR is fixed. + + Changes to code + + zic no longer mishandles a last transition to a new time type. + + zic no longer overflows a buffer when generating a TZ string like + "PST-167:59:58PDT-167:59:59,M11.5.6/-167:59:59,M12.5.6/-167:59:59", + which can occur with adversarial input. (Thanks to Naveed Khan.) + + zic no longer generates a longer TZif file than necessary when + an earlier time zone abbreviation is a suffix of a later one. + As a nice side effect, zic no longer overflows a buffer when given + a long series of abbreviations, each a suffix of the next. + (Buffer overflow reported by Arthur Chan.) + + zic no longer overflows an int when processing input like ‘Zone + Ouch 2147483648:00:00 - LMT’. The int overflow can lead to buffer + overflow in adversarial cases. (Thanks to Naveed Khan.) + + zic now checks for signals more often. + + Release 2026a - 2026-03-01 22:59:49 -0800 Briefly: Moldova has used EU transition times since 2022. - The "right" TZif files are no longer installed by default. + The “right” TZif files are no longer installed by default. -DTZ_RUNTIME_LEAPS=0 disables runtime support for leap seconds. TZif files are no longer limited to 50 bytes of abbreviations. zic is no longer limited to 50 leap seconds. @@ -25,23 +62,23 @@ Release 2026a - 2026-03-01 22:59:49 -0800 The Makefile no longer by default installs an alternate set of TZif files for system clocks that count leap seconds. - Install with 'make REDO=posix_right' to get the old default, + Install with ‘make REDO=posix_right’ to get the old default, which is rarely used in major downstream distributions. If your system clock counts leap seconds (contrary to POSIX), - it is better to install with 'make REDO=right_only'. + it is better to install with ‘make REDO=right_only’. This change does not affect the leapseconds file, which is still installed as before. - The Makefile's POSIXRULES option, which was declared obsolete in - release 2019b, has been removed. The Makefile's build procedure + The Makefile’s POSIXRULES option, which was declared obsolete in + release 2019b, has been removed. The Makefile’s build procedure thus no longer optionally installs the obsolete posixrules file. Changes to code Compiling with the new option -DTZ_RUNTIME_LEAPS=0 disables runtime support for leap seconds. Although this conforms to - POSIX, shrinks tzcode's attack surface, and is more efficient, - it fails to support Internet RFC 9636's leap seconds. + POSIX, shrinks tzcode’s attack surface, and is more efficient, + it fails to support Internet RFC 9636’s leap seconds. zic now can generate, and localtime.c can now use, TZif files that hold up to 256 bytes of abbreviations, counting trailing NULs. @@ -51,7 +88,7 @@ Release 2026a - 2026-03-01 22:59:49 -0800 zic -L can now generate TZif files with more than 50 leap seconds. This helps test TZif readers not limited to 50 leap seconds, as - tzcode's localtime.c is; it has little immediate need for + tzcode’s localtime.c is; it has little immediate need for practical timekeeping as there have been only 27 leap seconds and possibly there will be no more, due to planned changes to UTC. zic -v warns if its output exceeds the old 50-second limit. diff --git a/contrib/tzcode/tz-art.html b/contrib/tzcode/tz-art.html index 1bd1bf781166..7870a6187390 100644 --- a/contrib/tzcode/tz-art.html +++ b/contrib/tzcode/tz-art.html @@ -446,7 +446,7 @@ Includes the song “Does Anybody Really Know What Time It Is?”. </li> <li> Emanuele Arciuli, -<a href="https://neumarecords.org/ols/products/william-duckworth-the-time-curve-preludes"><em>The Time Curve Preludes</em></a> (2023). +<a href="https://williamduckworth.bandcamp.com/album/the-time-curve-preludes"><em>The Time Curve Preludes</em></a> (2023). Neuma 174, 44:46. The title piece, composed by <a href="https://en.wikipedia.org/wiki/William_Duckworth_(composer)">William diff --git a/contrib/tzcode/tz-link.html b/contrib/tzcode/tz-link.html index 5f1989f014b3..4d2c12b09452 100644 --- a/contrib/tzcode/tz-link.html +++ b/contrib/tzcode/tz-link.html @@ -441,7 +441,7 @@ transition in the <code><abbr>tz</abbr></code> database.</li> Database Parser</a> is a <a href="https://en.wikipedia.org/wiki/C++">C++</a> parser and runtime library with a <a -href="https://en.cppreference.com/w/cpp/chrono.html"><code>std::chrono</code> API</a> +href="https://en.cppreference.com/cpp/chrono"><code>std::chrono</code> API</a> that is a standard part of C++. It is freely available under the <abbr title="Massachusetts Institute of Technology">MIT</abbr> license.</li> @@ -1135,8 +1135,7 @@ Network Time Protocol Best Current Practices</a> applications requiring accurate <abbr>UTC</abbr> or civil time, and is intended for use only in single, well-controlled environments.</li> <li>The <a -href="https://pairlist6.pair.net/mailman/listinfo/leapsecs">Leap -Second Discussion List</a> covers <a +href="https://groups.io/g/LEAPSECS">LEAPSECS List</a> covers <a href="https://gge.ext.unb.ca/Resources/gpsworld.november99.pdf">McCarthy and Klepczynski’s 1999 proposal to discontinue leap seconds</a>, discussed further in @@ -1146,21 +1145,30 @@ leap second: its history and possible future</a>. might be redefined without Leap Seconds</a> gives pointers on this contentious issue. -The General Conference on Weights and Measures +The General Conference on Weights and Measures (CGPM) <a href="https://www.bipm.org/en/cgpm-2022/resolution-4">decided in 2022</a> to discontinue the use of leap seconds by 2035, and requested that no discontinuous adjustments be made to UTC for at least a century. The World Radiocommunication Conference <a href="https://www.itu.int/dms_pub/itu-r/opb/act/R-ACT-WRC.15-2023-PDF-E.pdf">resolved -in 2023</a> to cooperate with this process. One proposal to implement this +in 2023</a> to cooperate with this process. A draft <a +href="https://www.bipm.org/documents/d/guest/cgpm-2026-draft-resolutions">Resolution +C to make continuous UTC effective on 2027-05-20</a>, +and thereby discontinue leap seconds, +has been scheduled for the 28th CGPM starting 2026-10-13 in Paris. +One proposal to implement this would replace leap seconds with seven 13-second leap smears occurring once per decade until 2100, with leap smears after that gradually increasing in size. See: <ul> -<li>Levine J. <a href="https://www.preprints.org/manuscript/202406.0043">A +<li>Levine J. <a href="https://tf.nist.gov/general/pdf/3242.pdf">A proposal to change the leap-second adjustments to coordinated universal time</a>. <em>Metrologia.</em> 2024;61(5):055002. doi:<a -href="https://doi.org/10.1088/1681-7575/ad6266">10.1088/1681-7575/ad6266</a>.</li> +href="https://doi.org/10.1088/1681-7575/ad6266">10.1088/1681-7575/ad6266</a> +with followups in doi:<a +href="https://doi.org/10.1088/1681-7575/ade314">10.1088/1681-7575/ade314</a> +and doi:<a +href="https://doi.org/10.1088/1681-7575/ade315">10.1088/1681-7575/ade315</a>.</li> </ul> However, there is still no consensus on whether this is the best way to replace leap seconds. diff --git a/contrib/tzcode/version b/contrib/tzcode/version index 5d9126009e7f..75d34ee38931 100644 --- a/contrib/tzcode/version +++ b/contrib/tzcode/version @@ -1 +1 @@ -2026a +2026b diff --git a/contrib/tzcode/zic.c b/contrib/tzcode/zic.c index d1084b4a25e9..f4c1aa6d54e6 100644 --- a/contrib/tzcode/zic.c +++ b/contrib/tzcode/zic.c @@ -253,11 +253,13 @@ symlink(char const *target, char const *linkname) (errno = ENOTSUP, -1) #endif +static int addabbr(char[TZ_MAX_CHARS], int *, char const *); static void addtt(zic_t starttime, int type); static int addtype(zic_t, char const *, bool, bool, bool); -static void leapadd(zic_t, int, int); static void adjleap(void); static void associate(void); +static void checkabbr(char const *); +static void check_for_signal(void); static void dolink(const char *, const char *, bool); static int getfields(char *, char **, int); static zic_t gethms(const char * string, const char * errstring); @@ -270,11 +272,11 @@ static void inrule(char ** fields, int nfields); static bool inzcont(char ** fields, int nfields); static bool inzone(char ** fields, int nfields); static bool inzsub(char **, int, bool); -static int itssymlink(char const *, int *); static bool is_alpha(char a); +static int itssymlink(char const *, int *); +static void leapadd(zic_t, int, int); static char lowerit(char); static void mkdirs(char const *, bool); -static void newabbr(const char * abbr); static zic_t oadd(zic_t t1, zic_t t2); static zic_t omul(zic_t, zic_t); static void outzone(const struct zone * zp, ptrdiff_t ntzones); @@ -704,6 +706,7 @@ eat(int fnum, lineno num) ATTRIBUTE_FORMAT((printf, 1, 0)) static void verror(const char *const string, va_list args) { + check_for_signal(); /* ** Match the format of "cc" to allow sh users to ** zic ... 2>&1 | error -t "*" -v @@ -1074,6 +1077,7 @@ make_links(void) warning(_("link %s targeting link %s"), links[i].l_linkname, links[i].l_target); } + check_for_signal(); } } @@ -1391,6 +1395,7 @@ main(int argc, char **argv) for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j) continue; outzone(&zones[i], j - i); + check_for_signal(); } make_links(); if (lcltime != NULL) { @@ -1486,9 +1491,11 @@ get_rand_u64(void) static int nwords; if (!nwords) { ssize_t s; - do + for (;; check_for_signal()) { s = getrandom(entropy_buffer, sizeof entropy_buffer, 0); - while (s < 0 && errno == EINTR); + if (! (s < 0 && errno == EINTR)) + break; + } if (s < 0) nwords = -1; @@ -1519,7 +1526,7 @@ get_rand_u64(void) rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1, r = 0, rmax = 0; - do { + for (;; check_for_signal()) { uint_fast64_t rmax1 = rmax; if (rmod) { /* Avoid signed integer overflow on theoretical platforms @@ -1530,7 +1537,9 @@ get_rand_u64(void) rmax1 = nrand * rmax1 + rand_max; r = nrand * r + rand(); rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX; - } while (rmax < UINT_FAST64_MAX); + if (UINT_FAST64_MAX <= rmax) + break; + } return r; } @@ -1577,9 +1586,11 @@ random_dirent(char const **name, char **namealloc) *name = *namealloc = dst; } - do + for (;; check_for_signal()) { r = get_rand_u64(); - while (unfair_min <= r); + if (r < unfair_min) + break; + } for (i = 0; i < suffixlen; i++) { dst[dirlen + prefixlen + i] = alphabet[r % alphabetlen]; @@ -1622,7 +1633,7 @@ open_outfile(char const **outname, char **tempname) exit(EXIT_FAILURE); } - while (true) { + for (;; check_for_signal()) { int oflags = O_WRONLY | O_BINARY | O_CREAT | O_EXCL; int fd = open(*outname, oflags, creat_perms); int err; @@ -1736,8 +1747,6 @@ dolink(char const *target, char const *linkname, bool staysymlink) char const *outname = linkname; int targetissym = -2, linknameissym = -2; - check_for_signal(); - if (strcmp(target, "-") == 0) { if (remove(linkname) == 0 || errno == ENOENT || errno == ENOTDIR) return; @@ -1750,7 +1759,7 @@ dolink(char const *target, char const *linkname, bool staysymlink) } } - while (true) { + for (;; check_for_signal()) { if (linkat(AT_FDCWD, target, AT_FDCWD, outname, AT_SYMLINK_FOLLOW) == 0) { link_errno = 0; @@ -1802,7 +1811,7 @@ dolink(char const *target, char const *linkname, bool staysymlink) int symlink_errno = -1; if (contents) { - while (true) { + for (;; check_for_signal()) { if (symlink(contents, outname) == 0) { symlink_errno = 0; break; @@ -1833,7 +1842,7 @@ dolink(char const *target, char const *linkname, bool staysymlink) exit(EXIT_FAILURE); } tp = open_outfile(&outname, &tempname); - while ((c = getc(fp)) != EOF) + for (; (c = getc(fp)) != EOF; check_for_signal()) putc(c, tp); close_file(tp, directory, linkname, tempname); close_file(fp, directory, target, NULL); @@ -1957,7 +1966,7 @@ static bool inputline(FILE *fp, char *buf, ptrdiff_t bufsize) { ptrdiff_t linelen = 0, ch; - while ((ch = getc(fp)) != '\n') { + for (; (ch = getc(fp)) != '\n'; check_for_signal()) { if (ch < 0) { if (ferror(fp)) { error(_("input error")); @@ -2044,6 +2053,7 @@ infile(int fnum, char const *name) default: unreachable(); } } + check_for_signal(); } close_file(fp, NULL, filename(fnum), NULL); if (wantcont) @@ -2657,7 +2667,6 @@ writezone(const char *const name, const char *const string, char version, { register FILE * fp; register ptrdiff_t i, j; - register size_t u; register int pass; char *tempname = NULL; char const *outname = name; @@ -2939,30 +2948,27 @@ writezone(const char *const name, const char *const string, char version, : i == thisdefaulttype ? old0 : i] = thistypecnt++; - for (u = 0; u < sizeof indmap / sizeof indmap[0]; ++u) - indmap[u] = -1; thischarcnt = stdcnt = utcnt = 0; for (i = old0; i < typecnt; i++) { - register char * thisabbr; - if (omittype[i]) continue; if (ttisstds[i]) stdcnt = thistypecnt; if (ttisuts[i]) utcnt = thistypecnt; - if (indmap[desigidx[i]] >= 0) - continue; - thisabbr = &chars[desigidx[i]]; - for (j = 0; j < thischarcnt; ++j) - if (strcmp(&thischars[j], thisabbr) == 0) - break; - if (j == thischarcnt) { - strcpy(&thischars[thischarcnt], thisabbr); - thischarcnt += strlen(thisabbr) + 1; - } - indmap[desigidx[i]] = j; + addabbr(thischars, &thischarcnt, &chars[desigidx[i]]); } + + /* Now that all abbrevs have been added to THISCHARS, + it is safe to set INDMAP without worrying about + whether the abbrevs might move later. */ + for (i = 0; i < TZ_MAX_CHARS; i++) + indmap[i] = -1; + for (i = old0; i < typecnt; i++) + if (!omittype[i] && indmap[desigidx[i]] < 0) + indmap[desigidx[i]] = addabbr(thischars, &thischarcnt, + &chars[desigidx[i]]); + if (pass == 1 && !want_bloat()) { hicut = thisleapexpiry = false; pretranstype = -1; @@ -3185,11 +3191,11 @@ stringoffset(char *result, zic_t offset) offset /= SECSPERMIN; minutes = offset % MINSPERHOUR; offset /= MINSPERHOUR; - hours = offset; - if (hours >= HOURSPERDAY * DAYSPERWEEK) { + if (offset >= HOURSPERDAY * DAYSPERWEEK) { result[0] = '\0'; return 0; } + hours = offset; len += sprintf(result + len, "%d", hours); if (minutes != 0 || seconds != 0) { len += sprintf(result + len, ":%02d", minutes); @@ -3428,12 +3434,16 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) int nonTZlimtype = -1; zic_t max_year0; int defaulttype = -1; + int max_stringoffset_len = sizeof "-167:59:59" - 1; + int max_comma_stringrule_len = (sizeof ",M12.5.6/" - 1 + + max_stringoffset_len); check_for_signal(); /* This cannot overflow; see FORMAT_LEN_GROWTH_BOUND. */ max_abbr_len = 2 + max_format_len + max_abbrvar_len; - max_envvar_len = 2 * max_abbr_len + 5 * 9; + max_envvar_len = 2 * (max_abbr_len + max_stringoffset_len + + max_comma_stringrule_len); startbuf = xmalloc(max_abbr_len + 1); ab = xmalloc(max_abbr_len + 1); @@ -3534,7 +3544,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) startttisut); if (usestart) { addtt(starttime, type); - if (useuntil && nonTZlimtime < starttime) { + if (nonTZlimtime < starttime) { nonTZlimtime = starttime; nonTZlimtype = type; } @@ -3781,6 +3791,7 @@ static int addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut) { register int i, j; + int charcnt0; /* RFC 9636 section 3.2 specifies this range for utoff. */ if (! (-TWO_31_MINUS_1 <= utoff && utoff <= TWO_31_MINUS_1)) { @@ -3790,12 +3801,18 @@ addtype(zic_t utoff, char const *abbr, bool isdst, bool ttisstd, bool ttisut) if (!want_bloat()) ttisstd = ttisut = false; - for (j = 0; j < charcnt; ++j) - if (strcmp(&chars[j], abbr) == 0) - break; - if (j == charcnt) - newabbr(abbr); - else { + checkabbr(abbr); + + charcnt0 = charcnt; + j = addabbr(chars, &charcnt, abbr); + if (charcnt0 < charcnt) { + /* If an abbreviation was inserted, increment indexes no + earlier than the insert by the size of the insertion, + so that they continue to point to the same contents. */ + for (i = 0; i < typecnt; i++) + if (j <= desigidx[i]) + desigidx[i] += charcnt - charcnt0; + } else { /* If there's already an entry, return its index. */ for (i = 0; i < typecnt; i++) if (utoff == utoffs[i] && isdst == isdsts[i] && j == desigidx[i] @@ -4180,10 +4197,8 @@ will not work with pre-2004 versions of zic")); } static void -newabbr(const char *string) +checkabbr(char const *string) { - register int i; - if (strcmp(string, GRANDPARENTED) != 0) { register const char * cp; const char * mp; @@ -4202,13 +4217,50 @@ mp = _("time zone abbreviation differs from POSIX standard"); if (mp != NULL) warning("%s (%s)", mp, string); } - i = strnlen(string, TZ_MAX_CHARS - charcnt) + 1; - if (charcnt + i > TZ_MAX_CHARS) { - error(_("too many, or too long, time zone abbreviations")); - exit(EXIT_FAILURE); - } - strcpy(&chars[charcnt], string); - charcnt += i; +} + +/* Put into CHS, which currently contains *PNCHS bytes containing + NUL-terminated abbreviations none of which are suffixes of another, + the abbreviation ABBR including its trailing NUL. + If ABBR does not already appear in CHS, + possibly as a suffix of an existing abbreviation, + add ABBR to CHS, remove from CHS any abbreviation + that is a suffix of ABBR, and increment *PNCHS accordingly. + Return the index of ABBR after any modifications to CHS are made. + + If all abbreviations have already been added, this function + lets the caller look up the index of an existing abbreviation. */ +static int +addabbr(char chs[TZ_MAX_CHARS], int *pnchs, char const *abbr) +{ + int nchs = *pnchs; + int alen = strlen(abbr), nchs_incr = alen + 1; + int i; + for (i = 0; i < nchs; ) { + int clen = strlen(&chs[i]); + if (alen <= clen) { + /* If ABBR is a suffix of an abbreviation in CHS, + return the index of ABBR in CHS. */ + int isuff = i + (clen - alen); + if (memcmp(&chs[isuff], abbr, alen) == 0) + return isuff; + } else if (memcmp(&chs[i], &abbr[alen - clen], clen) == 0) { + /* An abbreviation in CHS is a substring of ABBR. + Replace it with ABBR, instead of the more-common + actions of appending ABBR or doing nothing. */ + nchs_incr = alen - clen; + break; + } + i += clen + 1; + } + if (TZ_MAX_CHARS < nchs + nchs_incr) { + error(_("too many, or too long, time zone abbreviations")); + exit(EXIT_FAILURE); + } + memmove(&chs[i + nchs_incr], &chs[i], nchs - i); + memcpy(&chs[i], abbr, nchs_incr); + *pnchs = nchs + nchs_incr; + return i; } /* Ensure that the directories of ARGNAME exist, by making any missinghome | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69f36f6c.3fb61.643fa71c>
