[tz] [PROPOSED 5/6] Fix theoretical integer overflow in zic.c

Paul Eggert eggert at cs.ucla.edu
Sun Nov 20 05:47:59 UTC 2022


* zic.c (FORMAT_LEN_GROWTH_BOUND): New constant.
(infile): Don’t allocate a local buffer so large that later size
calculations can overflow.
(getsave, inzsub, doabbr, stringzone): Prefer ptrdiff_t to
size_t where either will do, as signed overflow is easier
for a debugging implementation to check.
---
 zic.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/zic.c b/zic.c
index 752ac48..53351d0 100644
--- a/zic.c
+++ b/zic.c
@@ -34,6 +34,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 <direct.h>
 # include <io.h>
@@ -1634,7 +1637,10 @@ infile(int fnum, char const *name)
 	}
 	wantcont = false;
 	for (num = 1; ; ++num) {
-		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);
@@ -1748,7 +1754,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) {
@@ -1844,7 +1850,7 @@ inzsub(char **fields, int nfields, bool iscont)
 	register char *		cp;
 	char *			cp1;
 	struct zone z;
-	size_t format_len;
+	int format_len;
 	register int		i_stdoff, i_rule, i_format;
 	register int		i_untilyear, i_untilmonth;
 	register int		i_untilday, i_untiltime;
@@ -2747,13 +2753,13 @@ abbroffset(char *buf, zic_t offset)
 
 static char const disable_percent_s[] = "";
 
-static size_t
+static ptrdiff_t
 doabbr(char *abbr, struct zone const *zp, char const *letters,
        bool isdst, zic_t save, bool doquotes)
 {
 	register char *	cp;
 	register char *	slashp;
-	register size_t	len;
+	ptrdiff_t len;
 	char const *format = zp->z_format;
 
 	slashp = strchr(format, '/');
@@ -2919,9 +2925,9 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
 	register ptrdiff_t		i;
 	register int			compat = 0;
 	register 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];
@@ -3054,8 +3060,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 = emalloc(max_abbr_len + 1);
 	ab = emalloc(max_abbr_len + 1);
 	envvar = emalloc(max_envvar_len + 1);
-- 
2.38.1



More information about the tz mailing list