* NEWS: Mention this. * date.c (dogmt, timeout): Limit allocations to at most PTRDIFF_MAX bytes to avoid undefined behavior on pointer subtraction. (dogmt): Work even if ‘environ’ has more than INT_MAX entries. (timeout): Avoid unnecessary pointer test, as the caller does this. Avoid unnecessary copy of struct tm. Don’t infloop on size_t overflow. Double size of array if it’s too small, to avoid O(N**2) CPU. * private.h (PTRDIFF_MAX): Move default value here ... * zic.c: ... from here, since date.c now uses it. --- NEWS | 3 +++ date.c | 32 +++++++++++--------------------- private.h | 4 ++++ zic.c | 5 ----- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/NEWS b/NEWS index 240bae8..7c46d3c 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ News for the tz database C89 is now deprecated; please use C99 or later. Portability fixes for AIX, libintl, MS-Windows, musl, z/OS C23 timegm now supported by default + Fixes for unlikely integer overflows Unreleased, experimental changes @@ -54,6 +55,8 @@ Unreleased, experimental changes uninitialized data has undefined behavior (strftime problem reported by Robert Elz). + Check more carefully for unlikely integer overflows. + Changes to build procedure New Makefile rule check_mild that skips checking whether Link diff --git a/date.c b/date.c index 4e4b355..cbc0ec1 100644 --- a/date.c +++ b/date.c @@ -117,14 +117,13 @@ dogmt(void) static char ** fakeenv; if (fakeenv == NULL) { - register int from; - register int to; - register int n; static char tzeutc0[] = "TZ=UTC0"; + ptrdiff_t from, to, n; for (n = 0; environ[n] != NULL; ++n) continue; - fakeenv = malloc((n + 2) * sizeof *fakeenv); + if (n <= min(PTRDIFF_MAX, SIZE_MAX) / sizeof *fakeenv - 2) + fakeenv = malloc((n + 2) * sizeof *fakeenv); if (fakeenv == NULL) { fprintf(stderr, _("date: Memory exhausted\n")); errensure(); @@ -183,33 +182,24 @@ display(char const *format, time_t now) static void timeout(FILE *fp, char const *format, struct tm const *tmp) { - char * cp; - size_t result; - size_t size; - struct tm tm; - int INCR = 1024; + char *cp = NULL; + ptrdiff_t result; + ptrdiff_t size = 1024 / 2; - if (!tmp) { - fprintf(stderr, _("date: error: time out of range\n")); - errensure(); - return; - } - tm = *tmp; - tmp = &tm; - size = INCR; - cp = malloc(size); for ( ; ; ) { - if (cp == NULL) { + bool bigger = (size <= min(PTRDIFF_MAX, SIZE_MAX) / 2 + && (size *= 2, true)); + char *newcp = bigger ? realloc(cp, size) : NULL; + if (!newcp) { fprintf(stderr, _("date: error: can't get memory\n")); errensure(); exit(retval); } + cp = newcp; result = strftime(cp, size, format, tmp); if (result != 0) break; - size += INCR; - cp = realloc(cp, size); } fwrite(cp + 1, 1, result - 1, fp); free(cp); diff --git a/private.h b/private.h index bdadd61..4315a85 100644 --- a/private.h +++ b/private.h @@ -353,6 +353,10 @@ 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 diff --git a/zic.c b/zic.c index 2db5486..752ac48 100644 --- a/zic.c +++ b/zic.c @@ -63,11 +63,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) -- 2.38.1