[tz] [PROPOSED 3/6] date: integer overflow fixes
Paul Eggert
eggert at cs.ucla.edu
Sun Nov 20 05:47:57 UTC 2022
* 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
More information about the tz
mailing list