diff --git a/lib/libc/time/Makefile b/lib/libc/time/Makefile index 34cec49d3af4..afb9d538a203 100644 --- a/lib/libc/time/Makefile +++ b/lib/libc/time/Makefile @@ -196,6 +196,7 @@ PACKRATLIST= UTF8_LOCALE= en_US.utf8 # Non-default libraries needed to link. +# On some hosts, this should have -lintl unless CFLAGS has -DHAVE_GETTEXT=0. LDLIBS= # Add the following to the end of the "CFLAGS=" line as needed to override @@ -208,14 +209,18 @@ LDLIBS= # For example, N is 252460800 on AmigaOS. # -DHAVE_DECL_ASCTIME_R=0 if does not declare asctime_r # -DHAVE_DECL_ENVIRON if declares 'environ' +# -DHAVE_DECL_TIMEGM=0 if does not declare timegm # -DHAVE_DIRECT_H if mkdir needs (MS-Windows) -# -DHAVE_GENERIC=0 if _Generic does not work -# -DHAVE_GETRANDOM if getgrandom works (e.g., GNU/Linux)* -# -DHAVE_GETTEXT if 'gettext' works (e.g., GNU/Linux, FreeBSD, Solaris)* +# -DHAVE_GENERIC=0 if _Generic does not work* +# -DHAVE_GETRANDOM if getrandom works (e.g., GNU/Linux), +# -DHAVE_GETRANDOM=0 to avoid using getrandom +# -DHAVE_GETTEXT if gettext works (e.g., GNU/Linux, FreeBSD, Solaris), +# where LDLIBS also needs to contain -lintl on some hosts; +# -DHAVE_GETTEXT=0 to avoid using gettext # -DHAVE_INCOMPATIBLE_CTIME_R if your system's time.h declares # ctime_r and asctime_r incompatibly with the POSIX standard # (Solaris when _POSIX_PTHREAD_SEMANTICS is not defined). -# -DHAVE_INTTYPES_H if you have a non-C99 compiler with +# -DHAVE_INTTYPES_H=0 if does not work* # -DHAVE_LINK=0 if your system lacks a link function # -DHAVE_LOCALTIME_R=0 if your system lacks a localtime_r function # -DHAVE_LOCALTIME_RZ=0 if you do not want zdump to use localtime_rz @@ -225,15 +230,17 @@ LDLIBS= # functions like 'link' or variables like 'tzname' required by POSIX # -DHAVE_SETENV=0 if your system lacks the setenv function # -DHAVE_SNPRINTF=0 if your system lacks the snprintf function -# -DHAVE_STDINT_H if you have a non-C99 compiler with * +# -DHAVE_STDCKDINT_H=0 if neither nor substitutes like +# __builtin_add_overflow work* +# -DHAVE_STDINT_H=0 if does not work* # -DHAVE_STRFTIME_L if declares locale_t and strftime_l # -DHAVE_STRDUP=0 if your system lacks the strdup function # -DHAVE_STRTOLL=0 if your system lacks the strtoll function # -DHAVE_SYMLINK=0 if your system lacks the symlink function -# -DHAVE_SYS_STAT_H=0 if your compiler lacks a * +# -DHAVE_SYS_STAT_H=0 if does not work* # -DHAVE_TZSET=0 if your system lacks a tzset function -# -DHAVE_UNISTD_H=0 if your compiler lacks a * -# -DHAVE_UTMPX_H=0 if your compiler lacks a * +# -DHAVE_UNISTD_H=0 if does not work* +# -DHAVE_UTMPX_H=0 if does not work* # -Dlocale_t=XXX if your system uses XXX instead of locale_t # -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers # with external linkage, e.g., applications cannot define 'localtime'. @@ -280,7 +287,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \ -Wdeclaration-after-statement -Wdouble-promotion \ -Wduplicated-branches -Wduplicated-cond \ -Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \ - -Winit-self -Wlogical-op \ + -Wimplicit-fallthrough=5 -Winit-self -Wlogical-op \ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ -Wnull-dereference \ -Wold-style-definition -Woverlength-strings -Wpointer-arith \ @@ -293,7 +300,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \ -Wtrampolines -Wundef -Wuninitialized -Wunused-macros -Wuse-after-free=3 \ -Wvariadic-macros -Wvla -Wwrite-strings \ -Wno-address -Wno-format-nonliteral -Wno-sign-compare \ - -Wno-type-limits -Wno-unused-parameter + -Wno-type-limits # # If your system has a "GMT offset" field in its "struct tm"s # (or if you decide to add such a field in your system's "time.h" file), @@ -340,14 +347,11 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 -fno-common \ # If you want functions that were inspired by early versions of X3J11's work, # add # -DSTD_INSPIRED -# to the end of the "CFLAGS=" line. This arranges for the functions -# "offtime", "timelocal", "timegm", "timeoff", -# "posix2time", and "time2posix" to be added to the time conversion library. +# to the end of the "CFLAGS=" line. This arranges for the following +# functions to be added to the time conversion library. # "offtime" is like "gmtime" except that it accepts a second (long) argument # that gives an offset to add to the time_t when converting it. # "timelocal" is equivalent to "mktime". -# "timegm" is like "timelocal" except that it turns a struct tm into -# a time_t using UT (rather than local time as "timelocal" does). # "timeoff" is like "timegm" except that it accepts a second (long) argument # that gives an offset to use when converting to a time_t. # "posix2time" and "time2posix" are described in an included manual page. @@ -495,6 +499,11 @@ TARFLAGS= `if tar $(GNUTARFLAGS) --version >/dev/null 2>&1; \ # Flags to give 'gzip' when making a distribution. GZIPFLAGS= -9n +# When comparing .tzs files, use GNU diff's -F'^TZ=' option if supported. +# This makes it easier to see which Zone has been affected. +DIFF_TZS= diff -u$$(! diff -u -F'^TZ=' - - <>/dev/null >&0 2>&1 \ + || echo ' -F^TZ=') + ############################################################################### #MAKE= make @@ -773,7 +782,8 @@ tzselect: tzselect.ksh version chmod +x $@.out mv $@.out $@ -check: check_character_set check_white_space check_links \ +check: check_back check_mild +check_mild: check_character_set check_white_space check_links \ check_name_lengths check_slashed_abbrs check_sorted \ check_tables check_web check_ziguard check_zishrink check_tzs @@ -824,16 +834,19 @@ check_slashed_abbrs: $(TDATA_TO_CHECK) CHECK_CC_LIST = { n = split($$1,a,/,/); for (i=2; i<=n; i++) print a[1], a[i]; } check_sorted: backward backzone iso3166.tab zone.tab zone1970.tab - $(AWK) '/^Link/ {printf "%.5d %s\n", g, $$3} /^$$/ {g++}' \ + $(AWK) '/^Link/ {printf "%.5d %s\n", g, $$3} !/./ {g++}' \ backward | LC_ALL=C sort -cu $(AWK) '/^Zone/ {print $$2}' backzone | LC_ALL=C sort -cu touch $@ -check_links: checklinks.awk $(TDATA_TO_CHECK) tzdata.zi +check_back: checklinks.awk $(TDATA_TO_CHECK) $(AWK) \ -v DATAFORM=$(DATAFORM) \ -v backcheck=backward \ -f checklinks.awk $(TDATA_TO_CHECK) + touch $@ + +check_links: checklinks.awk tzdata.zi $(AWK) \ -v DATAFORM=$(DATAFORM) \ -f checklinks.awk tzdata.zi @@ -849,7 +862,7 @@ check_tables: checktab.awk $(YDATA) backward $(ZONETABLES) check_tzs: $(TZS) $(TZS_NEW) if test -s $(TZS); then \ - diff -u $(TZS) $(TZS_NEW); \ + $(DIFF_TZS) $(TZS) $(TZS_NEW); \ else \ cp $(TZS_NEW) $(TZS); \ fi @@ -1050,7 +1063,7 @@ $(TIME_T_ALTERNATIVES): $(VERSION_DEPS) TZS_YEAR="$$range" TZS_CUTOFF_FLAG="-t $$range" \ D=$$wd/$@.dir \ to$$range.tzs) && \ - diff -u $(TIME_T_ALTERNATIVES_HEAD).dir/to$$range.tzs \ + $(DIFF_TZS) $(TIME_T_ALTERNATIVES_HEAD).dir/to$$range.tzs \ $@.dir/to$$range.tzs && \ if diff -q Makefile Makefile 2>/dev/null; then \ quiet_option='-q'; \ @@ -1220,7 +1233,7 @@ zdump.o: version.h zic.o: private.h tzfile.h version.h .PHONY: ALL INSTALL all -.PHONY: check check_time_t_alternatives +.PHONY: check check_mild check_time_t_alternatives .PHONY: check_web check_zishrink .PHONY: clean clean_misc dummy.zd force_tzs .PHONY: install install_data maintainer-clean names diff --git a/lib/libc/time/NEWS b/lib/libc/time/NEWS index 086151073a3b..701e490e4834 100644 --- a/lib/libc/time/NEWS +++ b/lib/libc/time/NEWS @@ -1,5 +1,91 @@ News for the tz database +Release 2022g - 2022-11-29 08:58:31 -0800 + + Briefly: + The northern edge of Chihuahua changes to US timekeeping. + Much of Greenland stops changing clocks after March 2023. + Fix some pre-1996 timestamps in northern Canada. + C89 is now deprecated; please use C99 or later. + Portability fixes for AIX, libintl, MS-Windows, musl, z/OS + In C code, use more C23 features if available. + C23 timegm now supported by default + Fixes for unlikely integer overflows + + Changes to future timestamps + + In the Mexican state of Chihuahua, the border strip near the US + will change to agree with nearby US locations on 2022-11-30. + The strip's western part, represented by Ciudad Juárez, switches + from -06 all year to -07/-06 with US DST rules, like El Paso, TX. + The eastern part, represented by Ojinaga, will observe US DST next + year, like Presidio, TX. (Thanks to Heitor David Pinto.) + A new Zone America/Ciudad_Juarez splits from America/Ojinaga. + + Much of Greenland, represented by America/Nuuk, stops observing + winter time after March 2023, so its daylight saving time becomes + standard time. (Thanks to Jonas Nyrup and Jürgen Appel.) + + Changes to past timestamps + + Changes for pre-1996 northern Canada (thanks to Chris Walton): + + Merge America/Iqaluit and America/Pangnirtung into the former, + with a backward compatibility link for the latter name. + There is no good evidence the two locations differ since 1970. + This change affects pre-1996 America/Pangnirtung timestamps. + + Cambridge Bay, Inuvik, Iqaluit, Rankin Inlet, Resolute and + Yellowknife did not observe DST in 1965, and did observe DST + from 1972 through 1979. + + Whitehorse moved from -09 to -08 on 1966-02-27, not 1967-05-28. + + Colombia's 1993 fallback was 02-06 24:00, not 04-04 00:00. + (Thanks to Alois Treindl.) + + Singapore's 1981-12-31 change was at 16:00 UTC (23:30 local time), + not 24:00 local time. (Thanks to Geoff Clare via Robert Elz.) + + Changes to code + + Although tzcode still works with C89, bugs found in recent routine + maintenance indicate that bitrot has set in and that in practice + C89 is no longer used to build tzcode. As it is a maintenance + burden, support for C89 is planned to be removed soon. Instead, + please use compilers compatible with C99, C11, C17, or C23. + + timegm, which tzcode implemented in 1989, will finally be + standardized 34 years later as part of C23, so timegm is now + supported even if STD_INSPIRED is not defined. + + Fix bug in zdump's tzalloc emulation on hosts that lack tm_zone. + (Problem reported by Đoàn Trần Công Danh.) + + Fix bug in zic on hosts where malloc(0) yields NULL on success. + (Problem reported by Tim McBrayer for AIX 6.1.) + + Fix zic configuration to avoid linkage failures on some platforms. + (Problems reported by Gilmore Davidson and Igor Ivanov.) + + Work around MS-Windows nmake incompatibility with POSIX. + (Problem reported by Manuela Friedrich.) + + Port mktime and strftime to debugging platforms where accessing + uninitialized data has undefined behavior (strftime problem + reported by Robert Elz). + + Check more carefully for unlikely integer overflows, preferring + C23 to overflow checking by hand, as the latter has + had obscure bugs. + + Changes to build procedure + + New Makefile rule check_mild that skips checking whether Link + lines are in the file 'backward'. (Inspired by a suggestion from + Stephen Colebourne.) + + Release 2022f - 2022-10-28 18:04:57 -0700 Briefly: @@ -16,7 +102,7 @@ Release 2022f - 2022-10-28 18:04:57 -0700 In C code, use some C23 features if available. Remove no-longer-needed workaround for Qt bug 53071. - Changes to future timestamps. + Changes to future timestamps Mexico will no longer observe DST after 2022, except for areas near the US border that continue to observe US DST rules. @@ -24,6 +110,7 @@ Release 2022f - 2022-10-28 18:04:57 -0700 from -07 (-06 with DST) to year-round -06, thus not changing its clocks that day. The new law states that Chihuahua near the US border no longer observes US DST. + (Thanks to gera for the heads-up about Chihuahua.) Fiji will not observe DST in 2022/3. (Thanks to Shalvin Narayan.) For now, assume DST is suspended indefinitely. diff --git a/lib/libc/time/ctime.3 b/lib/libc/time/ctime.3 index 52335db993f1..7408064bbf6c 100644 --- a/lib/libc/time/ctime.3 +++ b/lib/libc/time/ctime.3 @@ -1,6 +1,7 @@ -.\" $NetBSD: ctime.3,v 1.68 2022/10/26 23:22:54 jschauma Exp $ +.\" $NetBSD: ctime.3,v 1.69 2022/12/11 17:57:23 christos Exp $ .\" -.\" XXX: License missing? +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. .\" .Dd October 22, 2022 .Dt CTIME 3 @@ -603,6 +604,4 @@ restricted to years in the range 1900 through 2099. To avoid this portability mess, new programs should use .Fn strftime instead. -.\" @(#)newctime.3 8.3 -.\" This file is in the public domain, so clarified as of .\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/localtime.c b/lib/libc/time/localtime.c index a5eb83113e0b..b24282156409 100644 --- a/lib/libc/time/localtime.c +++ b/lib/libc/time/localtime.c @@ -1,4 +1,4 @@ -/* $NetBSD: localtime.c,v 1.135 2022/10/29 13:55:50 christos Exp $ */ +/* $NetBSD: localtime.c,v 1.136 2022/12/11 17:57:23 christos Exp $ */ /* Convert timestamp from time_t to struct tm. */ @@ -12,7 +12,7 @@ #if 0 static char elsieid[] = "@(#)localtime.c 8.17"; #else -__RCSID("$NetBSD: localtime.c,v 1.135 2022/10/29 13:55:50 christos Exp $"); +__RCSID("$NetBSD: localtime.c,v 1.136 2022/12/11 17:57:23 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -485,8 +485,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend, #endif if (!doaccess) { char const *dot; - size_t namelen = strlen(name); - if (sizeof lsp->fullname - sizeof tzdirslash <= namelen) + if (sizeof lsp->fullname - sizeof tzdirslash <= strlen(name)) return ENAMETOOLONG; /* Create a string "TZDIR/NAME". Using sprintf here @@ -895,7 +894,7 @@ is_digit(char c) ** Return a pointer to that character. */ -static ATTRIBUTE_PURE const char * +static ATTRIBUTE_REPRODUCIBLE const char * getzname(register const char *strp) { register char c; @@ -916,7 +915,7 @@ getzname(register const char *strp) ** We don't do any checking here; checking is done later in common-case code. */ -static ATTRIBUTE_PURE const char * +static ATTRIBUTE_REPRODUCIBLE const char * getqzname(register const char *strp, const int delim) { register int c; @@ -1182,13 +1181,11 @@ tzparse(const char *name, struct state *sp, struct state *basep) { const char * stdname; const char * dstname; - size_t stdlen; - size_t dstlen; - size_t charcnt; 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; dstname = NULL; /* XXX gcc */ @@ -1211,7 +1208,7 @@ tzparse(const char *name, struct state *sp, struct state *basep) if (name == NULL) return false; charcnt = stdlen + 1; - if (sizeof sp->chars < charcnt) + if ((ptrdiff_t)sizeof sp->chars < charcnt) return false; if (basep) { if (0 < basep->timecnt) @@ -1242,7 +1239,7 @@ tzparse(const char *name, struct state *sp, struct state *basep) if (!dstlen) return false; charcnt += dstlen + 1; - if (sizeof sp->chars < charcnt) + if ((ptrdiff_t)sizeof sp->chars < charcnt) return false; if (*name != '\0' && *name != ',' && *name != ';') { name = getoffset(name, &dstoffset); @@ -1647,6 +1644,14 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, } result = localsub(sp, &newt, setname, tmp); if (result) { +#if defined ckd_add && defined ckd_sub + if (t < sp->ats[0] + ? ckd_sub(&result->tm_year, + result->tm_year, years) + : ckd_add(&result->tm_year, + result->tm_year, years)) + return NULL; +#else register int_fast64_t newy; newy = result->tm_year; @@ -1658,6 +1663,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname, return NULL; } result->tm_year = (int)newy; +#endif } return result; } @@ -1733,8 +1739,8 @@ localtime_r(const time_t * __restrict timep, struct tm *tmp) */ static struct tm * -gmtsub(struct state const *sp, time_t const *timep, int_fast32_t offset, - struct tm *tmp) +gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep, + int_fast32_t offset, struct tm *tmp) { register struct tm * result; @@ -1910,6 +1916,12 @@ timesub(const time_t *timep, int_fast32_t offset, y = newy; } +#ifdef ckd_add + if (ckd_add(&tmp->tm_year, y, -TM_YEAR_BASE)) { + errno = EOVERFLOW; + return NULL; + } +#else if (!TYPE_SIGNED(time_t) && y < TM_YEAR_BASE) { int signed_y = (int)y; tmp->tm_year = signed_y - TM_YEAR_BASE; @@ -1920,6 +1932,7 @@ timesub(const time_t *timep, int_fast32_t offset, errno = EOVERFLOW; return NULL; } +#endif tmp->tm_yday = idays; /* ** The "extra" mods below avoid overflow problems. @@ -2005,6 +2018,9 @@ ctime_rz(const timezone_t sp, const time_t * timep, char *buf) static bool increment_overflow(int *ip, int j) { +#ifdef ckd_add + return ckd_add(ip, *ip, j); +#else register int const i = *ip; /* @@ -2017,22 +2033,30 @@ increment_overflow(int *ip, int j) return true; *ip += j; return false; +#endif } static bool increment_overflow32(int_fast32_t *const lp, int const m) { +#ifdef ckd_add + return ckd_add(lp, *lp, m); +#else register int_fast32_t const l = *lp; if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) return true; *lp += m; return false; +#endif } static bool increment_overflow_time(__time_t *tp, int_fast32_t j) { +#ifdef ckd_add + return ckd_add(tp, *tp, j); +#else /* ** This is like ** 'if (! (TIME_T_MIN <= *tp + j && *tp + j <= TIME_T_MAX)) ...', @@ -2044,6 +2068,7 @@ increment_overflow_time(__time_t *tp, int_fast32_t j) return true; *tp += j; return false; +#endif } static bool @@ -2086,6 +2111,23 @@ tmcomp(register const struct tm *const atmp, return result; } +/* Copy to *DEST from *SRC. Copy only the members needed for mktime, + as other members might not be initialized. */ +static void +mktmcpy(struct tm *dest, struct tm const *src) +{ + dest->tm_sec = src->tm_sec; + dest->tm_min = src->tm_min; + dest->tm_hour = src->tm_hour; + dest->tm_mday = src->tm_mday; + dest->tm_mon = src->tm_mon; + dest->tm_year = src->tm_year; + dest->tm_isdst = src->tm_isdst; +#if defined TM_GMTOFF && ! UNINIT_TRAP + dest->TM_GMTOFF = src->TM_GMTOFF; +#endif +} + static time_t time2sub(struct tm *const tmp, struct tm *(*funcp)(struct state const *, time_t const *, @@ -2110,7 +2152,8 @@ time2sub(struct tm *const tmp, struct tm yourtm, mytm; *okayp = false; - yourtm = *tmp; + mktmcpy(&yourtm, tmp); + #ifdef NO_ERROR_IN_DST_GAP again: #endif @@ -2155,14 +2198,19 @@ again: goto out_of_range; } } +#ifdef ckd_add + if (ckd_add(&yourtm.tm_year, y, -TM_YEAR_BASE)) + return WRONG; +#else if (increment_overflow32(&y, -TM_YEAR_BASE)) goto out_of_range; if (! (INT_MIN <= y && y <= INT_MAX)) goto out_of_range; yourtm.tm_year = (int)y; +#endif if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) saved_seconds = 0; - else if (y + TM_YEAR_BASE < EPOCH_YEAR) { + else if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) { /* ** We can't set tm_sec to 0, because that might push the ** time below the minimum representable time. @@ -2455,7 +2503,6 @@ mktime(struct tm *tmp) } #ifdef STD_INSPIRED - time_t timelocal_z(const timezone_t sp, struct tm *const tmp) { @@ -2471,13 +2518,9 @@ timelocal(struct tm *tmp) tmp->tm_isdst = -1; /* in case it wasn't initialized */ return mktime(tmp); } - -time_t -timegm(struct tm *tmp) -{ - return timeoff(tmp, 0); -} - +#else +static +#endif time_t timeoff(struct tm *tmp, long offset) { @@ -2487,7 +2530,18 @@ timeoff(struct tm *tmp, long offset) return time1(tmp, gmtsub, gmtptr, (int_fast32_t)offset); } -#endif /* defined STD_INSPIRED */ +time_t +timegm(struct tm *tmp) +{ + time_t t; + struct tm tmcpy; + mktmcpy(&tmcpy, tmp); + tmcpy.tm_wday = -1; + t = timeoff(&tmcpy, 0); + if (0 <= tmcpy.tm_wday) + *tmp = tmcpy; + return t; +} static int_fast32_t leapcorr(struct state const *sp, time_t t) diff --git a/lib/libc/time/private.h b/lib/libc/time/private.h index b25311a650ba..d9778ee4712b 100644 --- a/lib/libc/time/private.h +++ b/lib/libc/time/private.h @@ -1,6 +1,6 @@ /* Private header for tzdb code. */ -/* $NetBSD: private.h,v 1.63 2022/11/17 17:35:25 jakllsch Exp $ */ +/* $NetBSD: private.h,v 1.64 2022/12/11 17:57:23 christos Exp $ */ #ifndef PRIVATE_H #define PRIVATE_H @@ -29,6 +29,10 @@ ** Thank you! */ +#ifndef __STDC_VERSION__ +# define __STDC_VERSION__ 0 +#endif + /* Define true, false and bool if they don't work out of the box. */ #if __STDC_VERSION__ < 199901 # define true 1 @@ -68,27 +72,13 @@ # endif #endif /* _Generic is buggy in pre-4.9 GCC. */ -#if !defined HAVE_GENERIC && defined __GNUC__ +#if !defined HAVE_GENERIC && defined __GNUC__ && !defined __STRICT_ANSI__ # define HAVE_GENERIC (4 < __GNUC__ + (9 <= __GNUC_MINOR__)) #endif #ifndef HAVE_GENERIC # define HAVE_GENERIC (201112 <= __STDC_VERSION__) #endif -#if defined __APPLE__ -# define HAVE_GETRANDOM false -#endif -#if !defined HAVE_GETRANDOM && defined __has_include -# if __has_include() -# define HAVE_GETRANDOM true -# else -# define HAVE_GETRANDOM false -# endif -#endif -#ifndef HAVE_GETRANDOM -# define HAVE_GETRANDOM (2 < __GLIBC__ + (25 <= __GLIBC_MINOR__)) -#endif - #if !defined HAVE_GETTEXT && defined __has_include # if __has_include() # define HAVE_GETTEXT true @@ -308,36 +298,36 @@ #endif /* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */ -#ifdef __LONG_LONG_MAX__ +#if defined __LONG_LONG_MAX__ && !defined __STRICT_ANSI__ # ifndef LLONG_MAX # define LLONG_MAX __LONG_LONG_MAX__ # endif # ifndef LLONG_MIN # define LLONG_MIN (-1 - LLONG_MAX) # endif +# ifndef ULLONG_MAX +# define ULLONG_MAX (LLONG_MAX * 2ull + 1) +# endif #endif #ifndef INT_FAST64_MAX -# ifdef LLONG_MAX -typedef long long int_fast64_t; -# define INT_FAST64_MIN LLONG_MIN -# define INT_FAST64_MAX LLONG_MAX -# else -# if LONG_MAX >> 31 < 0xffffffff -Please use a compiler that supports a 64-bit integer type (or wider); -you may need to compile with "-DHAVE_STDINT_H". -# endif -typedef long int_fast64_t; +# if 1 <= LONG_MAX >> 31 >> 31 +typedef long int_fast64_t; # define INT_FAST64_MIN LONG_MIN # define INT_FAST64_MAX LONG_MAX +# else +/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */ +typedef long long int_fast64_t; +# define INT_FAST64_MIN LLONG_MIN +# define INT_FAST64_MAX LLONG_MAX # endif #endif #ifndef PRIdFAST64 -# if INT_FAST64_MAX == LLONG_MAX -# define PRIdFAST64 "lld" -# else +# if INT_FAST64_MAX == LONG_MAX # define PRIdFAST64 "ld" +# else +# define PRIdFAST64 "lld" # endif #endif @@ -383,24 +373,27 @@ typedef long intmax_t; # endif #endif +#ifndef PTRDIFF_MAX +# define PTRDIFF_MAX MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t)) +#endif + #ifndef UINT_FAST32_MAX typedef unsigned long uint_fast32_t; #endif #ifndef UINT_FAST64_MAX -# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ -typedef unsigned long long uint_fast64_t; +# if 3 <= ULONG_MAX >> 31 >> 31 +typedef unsigned long uint_fast64_t; +# define UINT_FAST64_MAX ULONG_MAX # else -# if ULONG_MAX >> 31 >> 1 < 0xffffffff -Please use a compiler that supports a 64-bit integer type (or wider); -you may need to compile with "-DHAVE_STDINT_H". -# endif -typedef unsigned long uint_fast64_t; +/* If this fails, compile with -DHAVE_STDINT_H or with a better compiler. */ +typedef unsigned long long uint_fast64_t; +# define UINT_FAST64_MAX ULLONG_MAX # endif #endif #ifndef UINTMAX_MAX -# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ +# ifdef ULLONG_MAX typedef unsigned long long uintmax_t; # else typedef unsigned long uintmax_t; @@ -408,7 +401,7 @@ typedef unsigned long uintmax_t; #endif #ifndef PRIuMAX -# if defined ULLONG_MAX || defined __LONG_LONG_MAX__ +# ifdef ULLONG_MAX # define PRIuMAX "llu" # else # define PRIuMAX "lu" @@ -419,23 +412,114 @@ typedef unsigned long uintmax_t; # define SIZE_MAX ((size_t) -1) #endif +/* Support ckd_add, ckd_sub, ckd_mul on C23 or recent-enough GCC-like + hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */ +#if !defined HAVE_STDCKDINT_H && defined __has_include +# if __has_include() +# define HAVE_STDCKDINT_H true +# endif +#endif +#ifdef HAVE_STDCKDINT_H +# if HAVE_STDCKDINT_H +# include +# endif +#elif defined __EDG__ +/* Do nothing, to work around EDG bug . */ +#elif defined __has_builtin +# if __has_builtin(__builtin_add_overflow) +# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r) +# endif +# if __has_builtin(__builtin_sub_overflow) +# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r) +# endif +# if __has_builtin(__builtin_mul_overflow) +# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r) +# endif +#elif 7 <= __GNUC__ +# define ckd_add(r, a, b) __builtin_add_overflow(a, b, r) +# define ckd_sub(r, a, b) __builtin_sub_overflow(a, b, r) +# define ckd_mul(r, a, b) __builtin_mul_overflow(a, b, r) +#endif + #if 3 <= __GNUC__ -# define ATTRIBUTE_CONST __attribute__((__const__)) # define ATTRIBUTE_MALLOC __attribute__((__malloc__)) -# define ATTRIBUTE_PURE __attribute__((__pure__)) # define ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec)) #else -# define ATTRIBUTE_CONST /* empty */ # define ATTRIBUTE_MALLOC /* empty */ -# define ATTRIBUTE_PURE /* empty */ # define ATTRIBUTE_FORMAT(spec) /* empty */ #endif -#if !defined _Noreturn && __STDC_VERSION__ < 201112 -# if 2 < __GNUC__ + (8 <= __GNUC_MINOR__) -# define _Noreturn __attribute__((__noreturn__)) +#if (defined __has_c_attribute \ + && (202311 <= __STDC_VERSION__ || !defined __STRICT_ANSI__)) +# define HAVE_HAS_C_ATTRIBUTE true +#else +# define HAVE_HAS_C_ATTRIBUTE false +#endif + +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(fallthrough) +# define ATTRIBUTE_FALLTHROUGH [[fallthrough]] +# endif +#endif +#ifndef ATTRIBUTE_FALLTHROUGH +# if 7 <= __GNUC__ +# define ATTRIBUTE_FALLTHROUGH __attribute__((fallthrough)) # else -# define _Noreturn +# define ATTRIBUTE_FALLTHROUGH ((void) 0) +# endif +#endif + +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(maybe_unused) +# define ATTRIBUTE_MAYBE_UNUSED [[maybe_unused]] +# endif +#endif +#ifndef ATTRIBUTE_MAYBE_UNUSED +# if 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define ATTRIBUTE_MAYBE_UNUSED __attribute__((unused)) +# else +# define ATTRIBUTE_MAYBE_UNUSED /* empty */ +# endif +#endif + +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(noreturn) +# define ATTRIBUTE_NORETURN [[noreturn]] +# endif +#endif +#ifndef ATTRIBUTE_NORETURN +# if 201112 <= __STDC_VERSION__ +# define ATTRIBUTE_NORETURN _Noreturn +# elif 2 < __GNUC__ + (8 <= __GNUC_MINOR__) +# define ATTRIBUTE_NORETURN __attribute__((noreturn)) +# else +# define ATTRIBUTE_NORETURN /* empty */ +# endif +#endif + +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(reproducible) +# define ATTRIBUTE_REPRODUCIBLE [[reproducible]] +# endif +#endif +#ifndef ATTRIBUTE_REPRODUCIBLE +# if 3 <= __GNUC__ +# define ATTRIBUTE_REPRODUCIBLE __attribute__((pure)) +# else +# define ATTRIBUTE_REPRODUCIBLE /* empty */ +# endif +#endif + +#if HAVE_HAS_C_ATTRIBUTE +# if __has_c_attribute(unsequenced) +# define ATTRIBUTE_UNSEQUENCED [[unsequenced]] +# endif +#endif +#ifndef ATTRIBUTE_UNSEQUENCED +# if 3 <= __GNUC__ +# define ATTRIBUTE_UNSEQUENCED __attribute__((const)) +# else +# define ATTRIBUTE_UNSEQUENCED /* empty */ # endif #endif @@ -562,7 +646,7 @@ char *asctime(struct tm const *); char *asctime_r(struct tm const *restrict, char *restrict); char *ctime(time_t const *); char *ctime_r(time_t const *, char *); -double difftime(time_t, time_t) ATTRIBUTE_CONST; +double difftime(time_t, time_t) ATTRIBUTE_UNSEQUENCED; size_t strftime(char *restrict, size_t, char const *restrict, struct tm const *restrict); # if HAVE_STRFTIME_L @@ -575,9 +659,24 @@ struct tm *localtime(time_t const *); struct tm *localtime_r(time_t const *restrict, struct tm *restrict); time_t mktime(struct tm *); time_t time(time_t *); +time_t timegm(struct tm *); void tzset(void); #endif +#ifndef HAVE_DECL_TIMEGM +# if (202311 <= __STDC_VERSION__ \ + || defined __GLIBC__ || defined __tm_zone /* musl */ \ + || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ + || (defined __APPLE__ && defined __MACH__)) +# define HAVE_DECL_TIMEGM true +# else +# define HAVE_DECL_TIMEGM false +# endif +#endif +#if !HAVE_DECL_TIMEGM && !defined timegm +time_t timegm(struct tm *); +#endif + #if !HAVE_DECL_ASCTIME_R && !defined asctime_r extern char *asctime_r(struct tm const *restrict, char *restrict); #endif @@ -617,9 +716,6 @@ void tzsetwall(void); # if TZ_TIME_T || !defined offtime struct tm *offtime(time_t const *, long); # endif -# if TZ_TIME_T || !defined timegm -time_t timegm(struct tm *); -# endif # if TZ_TIME_T || !defined timelocal time_t timelocal(struct tm *); # endif @@ -637,6 +733,7 @@ time_t posix2time(time_t); /* Infer TM_ZONE on systems where this information is known, but suppress guessing if NO_TM_ZONE is defined. Similarly for TM_GMTOFF. */ #if (defined __GLIBC__ \ + || defined __tm_zone /* musl */ \ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \ || (defined __APPLE__ && defined __MACH__)) # if !defined TM_GMTOFF && !defined NO_TM_GMTOFF @@ -665,10 +762,10 @@ timezone_t tzalloc(char const *); void tzfree(timezone_t); # ifdef STD_INSPIRED # if TZ_TIME_T || !defined posix2time_z -time_t posix2time_z(timezone_t __restrict, time_t) ATTRIBUTE_PURE; +time_t posix2time_z(timezone_t __restrict, time_t) ATTRIBUTE_REPRODUCIBLE; # endif # if TZ_TIME_T || !defined time2posix_z -time_t time2posix_z(timezone_t __restrict, time_t) ATTRIBUTE_PURE; +time_t time2posix_z(timezone_t __restrict, time_t) ATTRIBUTE_REPRODUCIBLE; # endif # endif #endif diff --git a/lib/libc/time/strftime.c b/lib/libc/time/strftime.c index 8a9bad1eb6d0..a9d2584b5984 100644 --- a/lib/libc/time/strftime.c +++ b/lib/libc/time/strftime.c @@ -1,4 +1,4 @@ -/* $NetBSD: strftime.c,v 1.50 2022/08/16 10:56:21 christos Exp $ */ +/* $NetBSD: strftime.c,v 1.51 2022/12/11 17:57:23 christos Exp $ */ /* Convert a broken-down timestamp to a string. */ @@ -35,7 +35,7 @@ static char elsieid[] = "@(#)strftime.c 7.64"; static char elsieid[] = "@(#)strftime.c 8.3"; #else -__RCSID("$NetBSD: strftime.c,v 1.50 2022/08/16 10:56:21 christos Exp $"); +__RCSID("$NetBSD: strftime.c,v 1.51 2022/12/11 17:57:23 christos Exp $"); #endif #endif /* LIBC_SCCS and not lint */ @@ -135,7 +135,7 @@ strftime_z(const timezone_t sp, char * __restrict s, size_t maxsize, #if HAVE_STRFTIME_L size_t strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t, - locale_t locale) + ATTRIBUTE_MAYBE_UNUSED locale_t locale) { /* Just call strftime, as only the C locale is supported. */ return strftime(s, maxsize, format, t); @@ -374,12 +374,21 @@ label: time_t) + 1]; time_t mkt; - tm = *t; + tm.tm_sec = t->tm_sec; + tm.tm_min = t->tm_min; + tm.tm_hour = t->tm_hour; + tm.tm_mday = t->tm_mday; + tm.tm_mon = t->tm_mon; + tm.tm_year = t->tm_year; + tm.tm_isdst = t->tm_isdst; +#if defined TM_GMTOFF && ! UNINIT_TRAP + tm.TM_GMTOFF = t->TM_GMTOFF; +#endif mkt = mktime_z(sp, &tm); - /* There is no portable, definitive - test for whether whether mktime - succeeded, so treat (time_t) -1 as - the success that it might be. */ + /* If mktime fails, %s expands to the + value of (time_t) -1 as a failure + marker; this is better in practice + than strftime failing. */ /* CONSTCOND */ if (TYPE_SIGNED(time_t)) { intmax_t n = mkt; diff --git a/lib/libc/time/theory.html b/lib/libc/time/theory.html index 298db866b39c..75e347f0f9d1 100644 --- a/lib/libc/time/theory.html +++ b/lib/libc/time/theory.html @@ -60,7 +60,6 @@ with current and future timestamps in the traditional North American mountain time zone can choose from the timezones America/Denver which observes US-style daylight saving time (DST), -America/Mazatlan which observes Mexican-style DST, and America/Phoenix which does not observe DST. Applications that also deal with past timestamps in the mountain time zone can choose from over a dozen timezones, such as diff --git a/lib/libc/time/time2posix.3 b/lib/libc/time/time2posix.3 index 928af0f621e1..0a0a32e17305 100644 --- a/lib/libc/time/time2posix.3 +++ b/lib/libc/time/time2posix.3 @@ -1,4 +1,7 @@ -.\" $NetBSD: time2posix.3,v 1.22 2017/10/29 06:07:48 abhinav Exp $ +.\" $NetBSD: time2posix.3,v 1.23 2022/12/11 17:57:23 christos Exp $ +.\" @(#)time2posix.3 7.7 +.\" This file is in the public domain, so clarified as of +.\" 1996-06-05 by Arthur David Olson. .Dd October 6, 2014 .Dt TIME2POSIX 3 .Os @@ -63,8 +66,8 @@ However, POSIX gives an arithmetic expression for directly computing a value from a given date/time, and the same relationship is assumed by some (usually older) applications. Any programs creating/dissecting -.Va time_t Ns 's -using such a relationship will typically not handle intervals over +.Va time_t +values using such a relationship will typically not handle intervals over leap seconds correctly. .Pp The @@ -132,10 +135,10 @@ A leap second deletion would look like... [Note: posix2time(B+1) => A+0 or A+1] .Pp If leap-second support is not enabled, local -.Va time_t Ns 's +.Va time_t and POSIX -.Va time_t Ns 's -are equivalent, and both +.Va time_t +values are equivalent, and both .Fn time2posix and .Fn posix2time @@ -149,6 +152,3 @@ degenerate to the identity function. .Xr mktime_z 3 , .Xr time 3 , .Xr tzalloc 3 -.\" @(#)time2posix.3 7.7 -.\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson. diff --git a/lib/libc/time/tzfile.5 b/lib/libc/time/tzfile.5 index 515d19dd1d6b..20e03b41f68a 100644 --- a/lib/libc/time/tzfile.5 +++ b/lib/libc/time/tzfile.5 @@ -1,7 +1,8 @@ -.\" $NetBSD: tzfile.5,v 1.32 2022/08/16 11:07:40 christos Exp $ +.\" $NetBSD: tzfile.5,v 1.33 2022/12/11 17:57:23 christos Exp $ .\" +.\" @(#)tzfile.5 8.3 .\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov). +.\" 2009-05-17 by Arthur David Olson. .Dd August 16, 2022 .Dt TZFILE 5 .Os @@ -514,6 +515,3 @@ Future changes to the format may append more data. .%U https://doi.org/10.17487/RFC8536 .%R RFC 8536 .Re -.\" @(#)tzfile.5 8.3 -.\" This file is in the public domain, so clarified as of -.\" 1996-06-05 by Arthur David Olson. diff --git a/lib/libc/time/tzselect.8 b/lib/libc/time/tzselect.8 index 04223b3cc901..6d907dcacdd9 100644 --- a/lib/libc/time/tzselect.8 +++ b/lib/libc/time/tzselect.8 @@ -1,10 +1,12 @@ -.\" $NetBSD: tzselect.8,v 1.12 2022/08/16 11:07:40 christos Exp $ +.\" $NetBSD: tzselect.8,v 1.13 2022/12/11 17:57:23 christos Exp $ .\" +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. .TH TZSELECT 8 .SH NAME tzselect \- select a timezone .SH SYNOPSIS -.ie \n(.g .ds - \f(CW-\fP +.ie \n(.g .ds - \f(CR-\fP .el .ds - \- .ds d " degrees .ds m " minutes @@ -123,6 +125,4 @@ newctime(3), tzfile(5), zdump(8), zic(8) Applications should not assume that .BR tzselect 's output matches the user's political preferences. -.\" @(#)tzselect.8 8.2 -.\" This file is in the public domain, so clarified as of .\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/tzset.3 b/lib/libc/time/tzset.3 index c9f7aa3961ef..f5312561031a 100644 --- a/lib/libc/time/tzset.3 +++ b/lib/libc/time/tzset.3 @@ -1,4 +1,6 @@ -.\" $NetBSD: tzset.3,v 1.44 2022/12/04 11:25:09 uwe Exp $ +.\" $NetBSD: tzset.3,v 1.45 2022/12/11 17:57:23 christos Exp $ +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. .Dd Auguset 23, 2021 .Dt TZSET 3 .Os @@ -407,7 +409,7 @@ local timezone information directory .\" .It Pa /usr/share/zoneinfo/localtime .\" local timezone file .It Pa /usr/share/zoneinfo/posixrules -used with POSIX-style TZ's +used with POSIX-style TZ .It Pa /usr/share/zoneinfo/GMT for UTC leap seconds .El diff --git a/lib/libc/time/version b/lib/libc/time/version index 5c8fbb478a2c..b74fa117a223 100644 --- a/lib/libc/time/version +++ b/lib/libc/time/version @@ -1 +1 @@ -2022f +2022g diff --git a/lib/libc/time/zdump.8 b/lib/libc/time/zdump.8 index 4587c8237605..1eac839a2771 100644 --- a/lib/libc/time/zdump.8 +++ b/lib/libc/time/zdump.8 @@ -1,4 +1,8 @@ -.\" $NetBSD: zdump.8,v 1.21 2021/10/22 14:26:04 christos Exp $ +.\" $NetBSD: zdump.8,v 1.22 2022/12/11 17:57:23 christos Exp $ +.\" @(#)zdump.8 8.2 +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. +.TH zdump 8 .Dd October 22, 2021 .Dt ZDUMP 8 .Os @@ -217,6 +221,3 @@ introduction of UTC is problematic. .Xr localtime 3 , .Xr tzfile 5 , .Xr zic 8 -.\" @(#)zdump.8 8.2 -.\" This file is in the public domain, so clarified as of -.\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/zdump.c b/lib/libc/time/zdump.c index 3f4742b28c25..32308a9f64b8 100644 --- a/lib/libc/time/zdump.c +++ b/lib/libc/time/zdump.c @@ -1,4 +1,4 @@ -/* $NetBSD: zdump.c,v 1.58 2022/10/29 13:55:50 christos Exp $ */ +/* $NetBSD: zdump.c,v 1.59 2022/12/11 17:57:23 christos Exp $ */ /* Dump time zone data in a textual format. */ /* @@ -8,7 +8,7 @@ #include #ifndef lint -__RCSID("$NetBSD: zdump.c,v 1.58 2022/10/29 13:55:50 christos Exp $"); +__RCSID("$NetBSD: zdump.c,v 1.59 2022/12/11 17:57:23 christos Exp $"); #endif /* !defined lint */ #ifndef NETBSD_INSPIRED @@ -89,20 +89,20 @@ static time_t absolute_max_time = ? (((time_t) 1 << atime_shift) - 1 + ((time_t) 1 << atime_shift)) : -1); static size_t longest; -static char * progname; +static char const *progname; static bool warned; static bool errout; static char const *abbr(struct tm const *); -static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE; +static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_REPRODUCIBLE; static void dumptime(struct tm const *); -static time_t hunt(timezone_t, char *, time_t, time_t, bool); +static time_t hunt(timezone_t, time_t, time_t, bool); static void show(timezone_t, char *, time_t, bool); static void showextrema(timezone_t, char *, time_t, struct tm *, time_t); static void showtrans(char const *, struct tm const *, time_t, char const *, char const *); static const char *tformat(void); -static time_t yeartot(intmax_t) ATTRIBUTE_PURE; +static time_t yeartot(intmax_t) ATTRIBUTE_REPRODUCIBLE; /* Is C an ASCII digit? */ static bool @@ -130,14 +130,28 @@ is_alpha(char a) } } -/* Return A + B, exiting if the result would overflow. */ -static size_t +static ATTRIBUTE_NORETURN void +size_overflow(void) +{ + fprintf(stderr, _("%s: size overflow\n"), progname); + exit(EXIT_FAILURE); +} + +/* Return A + B, exiting if the result would overflow either ptrdiff_t + or size_t. */ +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t sumsize(size_t a, size_t b) { - size_t sum = a + b; - if (sum < a) - errx(EXIT_FAILURE, _("size overflow")); - return sum; +#ifdef ckd_add + ptrdiff_t sum; + if (!ckd_add(&sum, a, b) && sum <= PTRDIFF_MAX) + return sum; +#else + ptrdiff_t sum_max = min(PTRDIFF_MAX, SIZE_MAX); + if (a <= sum_max && b <= sum_max - a) + return a + b; +#endif + size_overflow(); } /* Return a pointer to a newly allocated buffer of size SIZE, exiting @@ -237,21 +251,29 @@ tzalloc(char const *val) exit(EXIT_FAILURE); } tzset(); - return NULL; + return &optarg; /* Any valid non-null char ** will do. */ # else enum { TZeqlen = 3 }; static char const TZeq[TZeqlen] = "TZ="; - static size_t fakeenv0size; + static ptrdiff_t fakeenv0size; void *freeable = NULL; char **env = fakeenv, **initial_environ; size_t valsize = strlen(val) + 1; if (fakeenv0size < valsize) { char **e = environ, **to; - ptrdiff_t initial_nenvptrs; /* Counting the trailing NULL pointer. */ + ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */ - while (*e++) - continue; - initial_nenvptrs = e - environ; + while (*e++) { +# ifdef ckd_add + if (ckd_add(&initial_nenvptrs, initial_envptrs, 1) + || SIZE_MAX < initial_envptrs) + size_overflow(); +# else + if (initial_nenvptrs == min(PTRDIFF_MAX, SIZE_MAX) / sizeof *environ) + size_overflow(); + initial_nenvptrs++; +# endif ++ } fakeenv0size = sumsize(valsize, valsize); fakeenv0size = max(fakeenv0size, 64); freeable = env; @@ -386,14 +408,14 @@ abbrok(const char *const abbrp, const char *const zone) return the abbreviation. Get the abbreviation from TMP. Exit on memory allocation failure. */ static char const * -saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp) +saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp) { char const *ab = abbr(tmp); if (HAVE_LOCALTIME_RZ) return ab; else { size_t ablen = strlen(ab); - if (*bufalloc <= ablen) { + if (*bufalloc <= (ptrdiff_t)ablen) { free(*buf); /* Make the new buffer at least twice as long as the @@ -441,7 +463,7 @@ main(int argc, char *argv[]) { /* These are static so that they're initially zero. */ static char * abbrev; - static size_t abbrevsize; + static ptrdiff_t abbrevsize; int i; bool vflag; @@ -462,7 +484,7 @@ main(int argc, char *argv[]) # endif /* defined TEXTDOMAINDIR */ (void) textdomain(TZ_DOMAIN); #endif /* HAVE_GETTEXT */ - progname = argv[0]; + progname = argv[0] ? argv[0] : "zdump"; for (i = 1; i < argc; ++i) if (strcmp(argv[i], "--version") == 0) { (void) printf("zdump %s%s\n", PKGVERSION, TZVERSION); @@ -482,7 +504,7 @@ main(int argc, char *argv[]) case -1: if (! (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) goto arg_processing_done; - /* Fall through. */ + ATTRIBUTE_FALLTHROUGH; default: usage(stderr, EXIT_FAILURE); } @@ -606,7 +628,7 @@ main(int argc, char *argv[]) || (ab && (delta(&newtm, &tm) != newt - t || newtm.tm_isdst != tm.tm_isdst || strcmp(abbr(&newtm), ab) != 0))) { - newt = hunt(tz, argv[i], t, newt, false); + newt = hunt(tz, t, newt, false); newtmp = localtime_rz(tz, &newt, &newtm); newtm_ok = newtmp != NULL; if (iflag) @@ -687,7 +709,7 @@ yeartot(intmax_t y) return t; } -/* Search for a discontinuity in timezone TZ with name NAME, in the +/* Search for a discontinuity in timezone TZ, in the timestamps ranging from LOT through HIT. LOT and HIT disagree about some aspect of timezone. If ONLY_OK, search only for definedness changes, i.e., localtime succeeds on one side of the @@ -695,10 +717,10 @@ yeartot(intmax_t y) before the transition from LOT's settings. */ static time_t -hunt(timezone_t tz, char *name, time_t lot, time_t hit, bool only_ok) +hunt(timezone_t tz, time_t lot, time_t hit, bool only_ok) { static char * loab; - static size_t loabsize; + static ptrdiff_t loabsize; struct tm lotm; struct tm tm; @@ -787,7 +809,8 @@ adjusted_yday(struct tm const *a, struct tm const *b) my_gmtime_r and use its result instead of B. Otherwise, B is the possibly nonnull result of an earlier call to my_gmtime_r. */ static long -gmtoff(struct tm const *a, time_t *t, struct tm const *b) +gmtoff(struct tm const *a, ATTRIBUTE_MAYBE_UNUSED time_t *t, + ATTRIBUTE_MAYBE_UNUSED struct tm const *b) { #ifdef TM_GMTOFF return a->TM_GMTOFF; @@ -858,7 +881,7 @@ static void showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi) { struct tm localtm[2], gmtm[2]; - time_t t, boundary = hunt(tz, zone, lo, hi, true); + time_t t, boundary = hunt(tz, lo, hi, true); bool old = false; hi = (SECSPERDAY < hi - boundary ? boundary + SECSPERDAY @@ -937,7 +960,7 @@ my_snprintf(char *s, size_t size, char const *format, ...) fit, return the length that the string would have been if it had fit; do not overrun the output buffer. */ static int -format_local_time(char *buf, size_t size, struct tm const *tm) +format_local_time(char *buf, ptrdiff_t size, struct tm const *tm) { int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour; return (ss @@ -960,7 +983,7 @@ format_local_time(char *buf, size_t size, struct tm const *tm) the length that the string would have been if it had fit; do not overrun the output buffer. */ static int -format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t) +format_utc_offset(char *buf, ptrdiff_t size, struct tm const *tm, time_t t) { long off = gmtoff(tm, &t, NULL); char sign = ((off < 0 @@ -989,11 +1012,11 @@ format_utc_offset(char *buf, size_t size, struct tm const *tm, time_t t) If the representation's length is less than SIZE, return the length; the representation is not null terminated. Otherwise return SIZE, to indicate that BUF is too small. */ -static size_t -format_quoted_string(char *buf, size_t size, char const *p) +static ptrdiff_t +format_quoted_string(char *buf, ptrdiff_t size, char const *p) { char *b = buf; - size_t s = size; + ptrdiff_t s = size; if (!s) return size; *b++ = '"', s--; @@ -1031,11 +1054,11 @@ format_quoted_string(char *buf, size_t size, char const *p) and omit any trailing tabs. */ static bool -istrftime(char *buf, size_t size, char const *time_fmt, +istrftime(char *buf, ptrdiff_t size, char const *time_fmt, struct tm const *tm, time_t t, char const *ab, char const *zone_name) { char *b = buf; - size_t s = size; + ptrdiff_t s = size; char const *f = time_fmt, *p; for (p = f; ; p++) @@ -1044,11 +1067,11 @@ istrftime(char *buf, size_t size, char const *time_fmt, else if (!*p || (*p == '%' && (p[1] == 'f' || p[1] == 'L' || p[1] == 'Q'))) { - size_t formatted_len; - size_t f_prefix_len = p - f; - size_t f_prefix_copy_size = p - f + 2; + ptrdiff_t formatted_len; + ptrdiff_t f_prefix_len = p - f; + ptrdiff_t f_prefix_copy_size = sumsize(f_prefix_len, 2); char fbuf[100]; - bool oversized = sizeof fbuf <= f_prefix_copy_size; + bool oversized = (ptrdiff_t)sizeof fbuf <= f_prefix_copy_size; char *f_prefix_copy = oversized ? xmalloc(f_prefix_copy_size) : fbuf; memcpy(f_prefix_copy, f, f_prefix_len); strcpy(f_prefix_copy + f_prefix_len, "X"); @@ -1072,20 +1095,20 @@ istrftime(char *buf, size_t size, char const *time_fmt, { bool show_abbr; int offlen = format_utc_offset(b, s, tm, t); - if (! (0 <= offlen && (size_t)offlen < s)) + if (! (0 <= offlen && offlen < s)) return false; show_abbr = strcmp(b, ab) != 0; b += offlen, s -= offlen; if (show_abbr) { char const *abp; - size_t len; + ptrdiff_t len; if (s <= 1) return false; *b++ = '\t', s--; for (abp = ab; is_alpha(*abp); abp++) continue; len = (!*abp && *ab - ? (size_t)my_snprintf(b, s, "%s", ab) + ? my_snprintf(b, s, "%s", ab) : format_quoted_string(b, s, ab)); if (s <= len) return false; @@ -1117,7 +1140,7 @@ showtrans(char const *time_fmt, struct tm const *tm, time_t t, char const *ab, putchar('\n'); } else { char stackbuf[1000]; - size_t size = sizeof stackbuf; + ptrdiff_t size = sizeof stackbuf; char *buf = stackbuf; char *bufalloc = NULL; while (! istrftime(buf, size, time_fmt, tm, t, ab, zone_name)) { diff --git a/lib/libc/time/zic.8 b/lib/libc/time/zic.8 index e02290a45d65..80c7e158d15b 100644 --- a/lib/libc/time/zic.8 +++ b/lib/libc/time/zic.8 @@ -1,4 +1,8 @@ -.\" $NetBSD: zic.8,v 1.40 2022/10/29 13:55:50 christos Exp $ +.\" $NetBSD: zic.8,v 1.41 2022/12/11 17:57:23 christos Exp $ +.\" @(#)zic.8 8.6 +.\" This file is in the public domain, so clarified as of +.\" 2009-05-17 by Arthur David Olson. +.TH zic 8 .Dd August 24, 2022 .Dt ZIC 8 .Os @@ -842,6 +846,3 @@ specifying transition instants using universal time. .Sh SEE ALSO .Xr tzfile 5 , .Xr zdump 8 -.\" @(#)zic.8 8.6 -.\" This file is in the public domain, so clarified as of -.\" 2009-05-17 by Arthur David Olson. diff --git a/lib/libc/time/zic.c b/lib/libc/time/zic.c index 4ab642ce20e7..04b4717242bb 100644 --- a/lib/libc/time/zic.c +++ b/lib/libc/time/zic.c @@ -1,4 +1,4 @@ -/* $NetBSD: zic.c,v 1.85 2022/11/02 12:49:10 christos Exp $ */ +/* $NetBSD: zic.c,v 1.86 2022/12/11 17:57:23 christos Exp $ */ /* ** This file is in the public domain, so clarified as of ** 2006-07-17 by Arthur David Olson. @@ -11,7 +11,7 @@ #include #ifndef lint -__RCSID("$NetBSD: zic.c,v 1.85 2022/11/02 12:49:10 christos Exp $"); +__RCSID("$NetBSD: zic.c,v 1.86 2022/12/11 17:57:23 christos Exp $"); #endif /* !defined lint */ /* Use the system 'time' function, instead of any private replacement. @@ -31,6 +31,9 @@ __RCSID("$NetBSD: zic.c,v 1.85 2022/11/02 12:49:10 christos Exp $"); #include #include #include +#define emalloc zic_malloc +#define erealloc zic_realloc +#define estrdup zic_strdup typedef int_fast64_t zic_t; static zic_t const @@ -44,6 +47,9 @@ static zic_t const # define ZIC_MAX_ABBR_LEN_WO_WARN 6 #endif /* !defined ZIC_MAX_ABBR_LEN_WO_WARN */ +/* An upper bound on how much a format might grow due to concatenation. */ +enum { FORMAT_LEN_GROWTH_BOUND = 5 }; + #ifdef HAVE_DIRECT_H # include # include @@ -51,7 +57,16 @@ static zic_t const # define mkdir(name, mode) _mkdir(name) #endif -#if HAVE_GETRANDOM +#ifndef HAVE_GETRANDOM +# ifdef __has_include +# if __has_include() +# include +# endif +# elif 2 < __GLIBC__ + (25 <= __GLIBC_MINOR__) +# include +# endif +# define HAVE_GETRANDOM GRND_RANDOM +#elif HAVE_GETRANDOM # include #endif @@ -64,11 +79,6 @@ static zic_t const # define MKDIR_UMASK 0755 #endif -/* The maximum ptrdiff_t value, for pre-C99 platforms. */ -#ifndef PTRDIFF_MAX -static ptrdiff_t const PTRDIFF_MAX = MAXVAL(ptrdiff_t, TYPE_BIT(ptrdiff_t)); -#endif - /* The minimum alignment of a type, for pre-C23 platforms. */ #if __STDC_VERSION__ < 201112 # define alignof(type) offsetof(struct { char a; type b; }, b) @@ -462,29 +472,54 @@ static char roll[TZ_MAX_LEAPS]; ** Memory allocation. */ -static _Noreturn void +static ATTRIBUTE_NORETURN void memory_exhausted(const char *msg) { fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg); exit(EXIT_FAILURE); } -static ATTRIBUTE_PURE size_t -size_product(size_t nitems, size_t itemsize) +static ATTRIBUTE_NORETURN void +size_overflow(void) { - if (SIZE_MAX / itemsize < nitems) - memory_exhausted(_("size overflow")); - return nitems * itemsize; + memory_exhausted(_("size overflow")); } -static ATTRIBUTE_PURE size_t -align_to(size_t size, size_t alignment) +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t +size_sum(size_t a, size_t b) { - size_t aligned_size = size + alignment - 1; - aligned_size -= aligned_size % alignment; - if (aligned_size < size) - memory_exhausted(_("alignment overflow")); - return aligned_size; +#ifdef ckd_add + ptrdiff_t sum; + if (!ckd_add(&sum, a, b) && sum <= PTRDIFF_MAX) + return sum; +#else + ptrdiff_t sum_max = min(PTRDIFF_MAX, SIZE_MAX); + if (a <= sum_max && b <= sum_max - a) + return a + b; +#endif + size_overflow(); +} + +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t +size_product(ptrdiff_t nitems, ptrdiff_t itemsize) +{ +#ifdef ckd_mul + ptrdiff_t product; + if (!ckd_mul(&product, nitems, itemsize) && product <= PTRDIFF_MAX) + return product; +#else + ptrdiff_t nitems_max = min(PTRDIFF_MAX, SIZE_MAX) / itemsize; + if (nitems <= nitems_max) + return nitems * itemsize; +#endif + size_overflow(); +} + +static ATTRIBUTE_REPRODUCIBLE ptrdiff_t +align_to(ptrdiff_t size, ptrdiff_t alignment) +{ + ptrdiff_t lo_bits = alignment - 1, sum = size_sum(size, lo_bits); + return sum & ~lo_bits; } #if !HAVE_STRDUP @@ -505,35 +540,49 @@ memcheck(void *ptr) } static void * ATTRIBUTE_MALLOC -zic_malloc(size_t size) +emalloc(size_t size) { return memcheck(malloc(size)); } static void * -zic_realloc(void *ptr, size_t size) +erealloc(void *ptr, size_t size) { return memcheck(realloc(ptr, size)); } static char * ATTRIBUTE_MALLOC -ecpyalloc(char const *str) +estrdup(char const *str) { return memcheck(strdup(str)); } -static void * -growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, ptrdiff_t *nitems_alloc) +static ptrdiff_t +grow_nitems_alloc(ptrdiff_t *nitems_alloc, ptrdiff_t itemsize) { - if (nitems < *nitems_alloc) - return ptr; - else { - ptrdiff_t amax = min(PTRDIFF_MAX, SIZE_MAX); - if ((amax - 1) / 3 * 2 < *nitems_alloc) - memory_exhausted(_("integer overflow")); - *nitems_alloc += (*nitems_alloc >> 1) + 1; - return zic_realloc(ptr, size_product(*nitems_alloc, itemsize)); - } + ptrdiff_t addend = (*nitems_alloc >> 1) + 1; +#if defined ckd_add && defined ckd_mul + ptrdiff_t product; + if (!ckd_add(nitems_alloc, *nitems_alloc, addend) + && !ckd_mul(&product, *nitems_alloc, itemsize) && product <= PTRDIFF_MAX) + return product; +#else + ptrdiff_t amax = min(PTRDIFF_MAX, SIZE_MAX); + if (*nitems_alloc <= ((amax - 1) / 3 * 2) / itemsize) { + *nitems_alloc += addend; + return *nitems_alloc * itemsize; + } +#endif + memory_exhausted(_("integer overflow")); +} + +static void * +growalloc(void *ptr, size_t itemsize, ptrdiff_t nitems, + ptrdiff_t *nitems_alloc) +{ + return (nitems < *nitems_alloc + ? ptr + : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize))); } /* @@ -630,7 +679,7 @@ close_file(FILE *stream, char const *dir, char const *name, } } -static _Noreturn void +static ATTRIBUTE_NORETURN void usage(FILE *stream, int status) { fprintf(stream, @@ -953,7 +1002,7 @@ main(int argc, char **argv) textdomain(TZ_DOMAIN); #endif /* HAVE_GETTEXT */ main_argv = argv; - progname = argv[0]; + progname = argv[0] ? argv[0] : "zic"; if (TYPE_BIT(zic_t) < 64) { fprintf(stderr, "%s: %s\n", progname, _("wild compilation-time specification of zic_t")); @@ -1214,21 +1263,12 @@ get_rand_u64(void) #endif /* getrandom didn't work, so fall back on portable code that is - not the best because the seed doesn't necessarily have enough bits, - the seed isn't cryptographically random on platforms lacking - getrandom, and 'rand' might not be cryptographically secure. */ + not the best because the seed isn't cryptographically random and + 'rand' might not be cryptographically secure. */ { static bool initialized; if (!initialized) { - unsigned seed; -#ifdef CLOCK_REALTIME - struct timespec now; - clock_gettime (CLOCK_REALTIME, &now); - seed = now.tv_sec ^ now.tv_nsec; -#else - seed = time(NULL); -#endif - srand(seed); + srand(time(NULL)); initialized = true; } } @@ -1237,13 +1277,21 @@ get_rand_u64(void) the typical case where RAND_MAX is one less than a power of two. In other cases this code yields a sort-of-random number. */ { - uint_fast64_t - rand_max = RAND_MAX, - multiplier = rand_max + 1, /* It's OK if this overflows to 0. */ + uint_fast64_t rand_max = RAND_MAX, + nrand = rand_max < UINT_FAST64_MAX ? rand_max + 1 : 0, + rmod = INT_MAX < UINT_FAST64_MAX ? 0 : UINT_FAST64_MAX / nrand + 1, r = 0, rmax = 0; + do { - uint_fast64_t rmax1 = rmax * multiplier + rand_max; - r = r * multiplier + rand(); + uint_fast64_t rmax1 = rmax; + if (rmod) { + /* Avoid signed integer overflow on theoretical platforms + where uint_fast64_t promotes to int. */ + rmax1 %= rmod; + r %= rmod; + } + rmax1 = nrand * rmax1 + rand_max; + r = nrand * r + rand(); rmax = rmax < rmax1 ? rmax1 : UINT_FAST64_MAX; } while (rmax < UINT_FAST64_MAX); @@ -1285,7 +1333,7 @@ random_dirent(char const **name, char **namealloc) uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6); if (!dst) { - dst = emalloc(dirlen + prefixlen + suffixlen + 1); + dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1)); memcpy(dst, src, dirlen); memcpy(dst + dirlen, prefix, prefixlen); dst[dirlen + prefixlen + suffixlen] = '\0'; @@ -1364,19 +1412,20 @@ rename_dest(char *tempname, char const *name) static char * relname(char const *target, char const *linkname) { - size_t i, taillen, dotdotetcsize; - size_t dir_len = 0, dotdots = 0, linksize = SIZE_MAX; + size_t i, taillen, dir_len = 0, dotdots = 0; + ptrdiff_t dotdotetcsize, linksize = min(PTRDIFF_MAX, SIZE_MAX); char const *f = target; char *result = NULL; if (*linkname == '/') { /* Make F absolute too. */ size_t len = strlen(directory); - bool needslash = len && directory[len - 1] != '/'; - linksize = len + needslash + strlen(target) + 1; + size_t lenslash = len + (len && directory[len - 1] != '/'); + size_t targetsize = strlen(target) + 1; + linksize = size_sum(lenslash, targetsize); f = result = emalloc(linksize); - strcpy(result, directory); + memcpy(result, directory, len); result[len] = '/'; - strcpy(result + len + needslash, target); + memcpy(result + lenslash, target, targetsize); } for (i = 0; f[i] && f[i] == linkname[i]; i++) if (f[i] == '/') @@ -1384,7 +1433,7 @@ relname(char const *target, char const *linkname) for (; linkname[i]; i++) dotdots += linkname[i] == '/' && linkname[i - 1] != '/'; taillen = strlen(f + dir_len); - dotdotetcsize = 3 * dotdots + taillen + 1; + dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1); if (dotdotetcsize <= linksize) { if (!result) result = emalloc(dotdotetcsize); @@ -1588,10 +1637,9 @@ associate(void) /* Read a text line from FP into BUF, which is of size BUFSIZE. Terminate it with a NUL byte instead of a newline. - Return the line's length, not counting the NUL byte. - On EOF, return a negative number. + Return true if successful, false if EOF. On error, report the error and exit. */ -static ptrdiff_t +static bool inputline(FILE *fp, char *buf, ptrdiff_t bufsize) { ptrdiff_t linelen = 0, ch; @@ -1602,7 +1650,7 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize) exit(EXIT_FAILURE); } if (linelen == 0) - return -1; + return false; error(_("unterminated line")); exit(EXIT_FAILURE); } @@ -1617,7 +1665,7 @@ inputline(FILE *fp, char *buf, ptrdiff_t bufsize) } } buf[linelen] = '\0'; - return linelen; + return true; } static void @@ -1639,13 +1687,14 @@ infile(int fnum, char const *name) } wantcont = false; for (num = 1; ; ++num) { - ptrdiff_t linelen; - char buf[_POSIX2_LINE_MAX]; + enum { bufsize_bound + = (min(INT_MAX, min(PTRDIFF_MAX, SIZE_MAX)) + / FORMAT_LEN_GROWTH_BOUND) }; + char buf[min(_POSIX2_LINE_MAX, bufsize_bound)]; int nfields; char *fields[MAX_FIELDS]; eat(fnum, num); - linelen = inputline(fp, buf, sizeof buf); - if (linelen < 0) + if (!inputline(fp, buf, sizeof buf)) break; nfields = getfields(buf, fields, sizeof fields / sizeof *fields); @@ -1717,15 +1766,15 @@ gethms(char const *string, char const *errstring) default: ok = false; break; case 8: ok = '0' <= xr && xr <= '9'; - /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; case 7: ok &= ssx == '.'; if (ok && noise) warning(_("fractional seconds rejected by" " pre-2018 versions of zic")); - /* fallthrough */ - case 5: ok &= mmx == ':'; /* fallthrough */ - case 3: ok &= hhx == ':'; /* fallthrough */ + ATTRIBUTE_FALLTHROUGH; + case 5: ok &= mmx == ':'; ATTRIBUTE_FALLTHROUGH; + case 3: ok &= hhx == ':'; ATTRIBUTE_FALLTHROUGH; case 1: break; } if (!ok) { @@ -1755,7 +1804,7 @@ getsave(char *field, bool *isdst) { int dst = -1; zic_t save; - size_t fieldlen = strlen(field); + ptrdiff_t fieldlen = strlen(field); if (fieldlen != 0) { char *ep = field + fieldlen - 1; switch (*ep) { @@ -1793,8 +1842,8 @@ inrule(char **fields, int nfields) fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD])) return; - r.r_name = ecpyalloc(fields[RF_NAME]); - r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]); + r.r_name = estrdup(fields[RF_NAME]); + r.r_abbrvar = estrdup(fields[RF_ABBRVAR]); if (max_abbrvar_len < strlen(r.r_abbrvar)) max_abbrvar_len = strlen(r.r_abbrvar); rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc); @@ -1851,7 +1900,7 @@ inzsub(char **fields, int nfields, bool iscont) char * cp; char * cp1; struct zone z; - size_t format_len; + int format_len; int i_stdoff, i_rule, i_format; int i_untilyear, i_untilmonth; int i_untilday, i_untiltime; @@ -1888,7 +1937,7 @@ inzsub(char **fields, int nfields, bool iscont) } z.z_format_specifier = cp ? *cp : '\0'; format_len = strlen(fields[i_format]); - if (max_format_len < format_len) + if ((ptrdiff_t)max_format_len < format_len) max_format_len = format_len; hasuntil = nfields > i_untilyear; if (hasuntil) { @@ -1918,9 +1967,9 @@ inzsub(char **fields, int nfields, bool iscont) return false; } } - z.z_name = iscont ? NULL : ecpyalloc(fields[ZF_NAME]); - z.z_rule = ecpyalloc(fields[i_rule]); - z.z_format = cp1 = ecpyalloc(fields[i_format]); + z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]); + z.z_rule = estrdup(fields[i_rule]); + z.z_format = cp1 = estrdup(fields[i_format]); if (z.z_format_specifier == 'z') { cp1[cp - fields[i_format]] = 's'; if (noise) @@ -1937,7 +1986,7 @@ inzsub(char **fields, int nfields, bool iscont) } static zic_t -getleapdatetime(char **fields, int nfields, bool expire_line) +getleapdatetime(char **fields, bool expire_line) { const char * cp; const struct lookup * lp; @@ -2015,7 +2064,7 @@ inleap(char **fields, int nfields) if (nfields != LEAP_FIELDS) error(_("wrong number of fields on Leap line")); else { - zic_t t = getleapdatetime(fields, nfields, false); + zic_t t = getleapdatetime(fields, false); if (0 <= t) { struct lookup const *lp = byword(fields[LP_ROLL], leap_types); if (!lp) @@ -2043,7 +2092,7 @@ inexpires(char **fields, int nfields) else if (0 <= leapexpires) error(_("multiple Expires lines")); else - leapexpires = getleapdatetime(fields, nfields, true); + leapexpires = getleapdatetime(fields, true); } static void @@ -2063,8 +2112,8 @@ inlink(char **fields, int nfields) return; l.l_filenum = filenum; l.l_linenum = linenum; - l.l_target = ecpyalloc(fields[LF_TARGET]); - l.l_linkname = ecpyalloc(fields[LF_LINKNAME]); + l.l_target = estrdup(fields[LF_TARGET]); + l.l_linkname = estrdup(fields[LF_LINKNAME]); links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc); links[nlinks++] = l; } @@ -2087,7 +2136,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, rp->r_month = lp->l_value; rp->r_todisstd = false; rp->r_todisut = false; - dp = ecpyalloc(timep); + dp = estrdup(timep); if (*dp != '\0') { ep = dp + strlen(dp) - 1; switch (lowerit(*ep)) { @@ -2166,7 +2215,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp, ** Sun<=20 ** Sun>=7 */ - dp = ecpyalloc(dayp); + dp = estrdup(dayp); if ((lp = byword(dp, lasts)) != NULL) { rp->r_dycode = DC_DOWLEQ; rp->r_wday = lp->l_value; @@ -2229,7 +2278,7 @@ convert64(uint_fast64_t val, char *buf) } static void -puttzcode(const int_fast32_t val, FILE *const fp) +puttzcode(zic_t val, FILE *fp) { char buf[4]; @@ -2318,8 +2367,10 @@ writezone(const char *const name, const char *const string, char version, char const *outname = name; /* Allocate the ATS and TYPES arrays via a single malloc, - as this is a bit faster. */ - zic_t *ats = emalloc(align_to(size_product(timecnt, sizeof *ats + 1), + as this is a bit faster. Do not malloc(0) if !timecnt, + as that might return NULL even on success. */ + zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt, + sizeof *ats + 1), alignof(zic_t))); void *typesptr = ats + timecnt; unsigned char *types = typesptr; @@ -2752,13 +2803,13 @@ abbroffset(char *buf, zic_t offset) static char const disable_percent_s[] = ""; -static size_t +static ptrdiff_t doabbr(char *abbr, size_t abbrlen, struct zone const *zp, const char *letters, bool isdst, zic_t save, bool doquotes) { char * cp; char * slashp; - size_t len; + ptrdiff_t len; char const *format = zp->z_format; slashp = strchr(format, '/'); @@ -2928,9 +2979,9 @@ stringzone(char *result, int resultlen, const struct zone *const zpfirst, ptrdiff_t i; int compat = 0; int c; - size_t len; int offsetlen; struct rule stdr, dstr; + ptrdiff_t len; int dstcmp; struct rule *lastrp[2] = { NULL, NULL }; struct zone zstr[2]; @@ -3064,8 +3115,10 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount) 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; + startbuf = zic_malloc(max_abbr_len + 1); ab = zic_malloc(max_abbr_len + 1); envvar = zic_malloc(max_envvar_len + 1); @@ -3572,7 +3625,7 @@ lowerit(char a) } /* case-insensitive equality */ -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool ciequal(const char *ap, const char *bp) { while (lowerit(*ap) == lowerit(*bp++)) @@ -3581,7 +3634,7 @@ ciequal(const char *ap, const char *bp) return false; } -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool itsabbr(const char *abbr, const char *word) { if (lowerit(*abbr) != lowerit(*word)) @@ -3597,7 +3650,7 @@ itsabbr(const char *abbr, const char *word) /* Return true if ABBR is an initial prefix of WORD, ignoring ASCII case. */ -static ATTRIBUTE_PURE bool +static ATTRIBUTE_REPRODUCIBLE bool ciprefix(char const *abbr, char const *word) { do @@ -3700,38 +3753,41 @@ getfields(char *cp, char **array, int arrayelts) return nsubs; } -static _Noreturn void +static ATTRIBUTE_NORETURN void time_overflow(void) { error(_("time overflow")); exit(EXIT_FAILURE); } -static ATTRIBUTE_PURE zic_t +static ATTRIBUTE_REPRODUCIBLE zic_t oadd(zic_t t1, zic_t t2) { - if (t1 < 0 ? t2 < ZIC_MIN - t1 : ZIC_MAX - t1 < t2) - time_overflow(); - return t1 + t2; +#ifdef ckd_add + zic_t sum; + if (!ckd_add(&sum, t1, t2)) + return sum; +#else + if (t1 < 0 ? ZIC_MIN - t1 <= t2 : t2 <= ZIC_MAX - t1) + return t1 + t2; +#endif + time_overflow(); } -static ATTRIBUTE_PURE zic_t +static ATTRIBUTE_REPRODUCIBLE zic_t tadd(zic_t t1, zic_t t2) { - if (t1 < 0) { - if (t2 < min_time - t1) { - if (t1 != min_time) - time_overflow(); - return min_time; - } - } else { - if (max_time - t1 < t2) { - if (t1 != max_time) - time_overflow(); - return max_time; - } - } - return t1 + t2; +#ifdef ckd_add + zic_t sum; + if (!ckd_add(&sum, t1, t2) && min_time <= sum && sum <= max_time) + return sum; +#else + if (t1 < 0 ? min_time - t1 <= t2 : t2 <= max_time - t1) + return t1 + t2; +#endif + if (t1 == min_time || t1 == max_time) + return t1; + time_overflow(); } /* @@ -3855,10 +3911,8 @@ mp = _("time zone abbreviation differs from POSIX standard"); static void mkdirs(char const *argname, bool ancestors) { - char * name; - char * cp; - - cp = name = ecpyalloc(argname); + char *name = estrdup(argname); + char *cp = name; /* On MS-Windows systems, do not worry about drive letters or backslashes, as this should suffice in practice. Time zone