[tz] more changes (and a bug fix)

Christos Zoulas christos at zoulas.com
Thu Oct 16 18:15:13 UTC 2014


Hi,

Most of these changes make sure that errno is always set appropriately
when the time functions fail.

The bug fix below that when lclptr is allocated, is not zeroed and
the result from zoneinit() was not checked. If zoneinit failed, then
lclptr would contain garbage, and settzname() would coredump because
it assumed that the lclptr contents were sane.

christos

--- localtime.c.orig	2014-10-07 16:20:32.000000000 -0400
+++ localtime.c	2014-10-16 14:10:03.000000000 -0400
@@ -690,17 +690,23 @@
 	register char	c;
 	register int	num;
 
-	if (strp == NULL || !is_digit(c = *strp))
+	if (strp == NULL || !is_digit(c = *strp)) {
+		errno = EINVAL;
 		return NULL;
+	}
 	num = 0;
 	do {
 		num = num * 10 + (c - '0');
-		if (num > max)
+		if (num > max) {
+			errno = ERANGE;
 			return NULL;	/* illegal value */
+		}
 		c = *++strp;
 	} while (is_digit(c));
-	if (num < min)
+	if (num < min) {
+		errno = ERANGE;
 		return NULL;		/* illegal value */
+	}
 	*nump = num;
 	return strp;
 }
@@ -1202,14 +1208,20 @@
       ? lcl_is_set < 0
       : 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
     return;
-  if (0 < lcl)
-    strcpy(lcl_TZname, name);
 #ifdef ALL_STATE
   if (! lclptr)
     lclptr = malloc(sizeof *lclptr);
 #endif /* defined ALL_STATE */
-  zoneinit(lclptr, name);
+  if (!zoneinit(lclptr, name)) {
+#ifdef ALL_STATE
+    free(lclptr);
+    lclptr = NULL;
+#endif
+    return;
+  }
   settzname();
+  if (0 < lcl)
+    strcpy(lcl_TZname, name);
   lcl_is_set = lcl;
 }
 
@@ -1328,8 +1340,10 @@
 				newt += seconds;
 			else	newt -= seconds;
 			if (newt < sp->ats[0] ||
-				newt > sp->ats[sp->timecnt - 1])
-					return NULL;	/* "cannot happen" */
+				newt > sp->ats[sp->timecnt - 1]) {
+				errno = EINVAL;
+				return NULL;	/* "cannot happen" */
+			}
 			result = localsub(sp, &newt, offset, tmp);
 			if (result) {
 				register int_fast64_t newy;
@@ -1338,8 +1352,10 @@
 				if (t < sp->ats[0])
 					newy -= years;
 				else	newy += years;
-				if (! (INT_MIN <= newy && newy <= INT_MAX))
+				if (! (INT_MIN <= newy && newy <= INT_MAX)) {
+					errno = EOVERFLOW;
 					return NULL;
+				}
 				result->tm_year = newy;
 			}
 			return result;
@@ -1528,13 +1544,13 @@
 		tdelta = tdays / DAYSPERLYEAR;
 		if (! ((! TYPE_SIGNED(time_t) || INT_MIN <= tdelta)
 		       && tdelta <= INT_MAX))
-			return NULL;
+			goto overflow;
 		idelta = tdelta;
 		if (idelta == 0)
 			idelta = (tdays < 0) ? -1 : 1;
 		newy = y;
 		if (increment_overflow(&newy, idelta))
-			return NULL;
+			goto overflow;
 		leapdays = leaps_thru_end_of(newy - 1) -
 			leaps_thru_end_of(y - 1);
 		tdays -= ((time_t) newy - y) * DAYSPERNYEAR;
@@ -1563,17 +1579,17 @@
 	}
 	while (idays < 0) {
 		if (increment_overflow(&y, -1))
-			return NULL;
+			goto overflow;
 		idays += year_lengths[isleap(y)];
 	}
 	while (idays >= year_lengths[isleap(y)]) {
 		idays -= year_lengths[isleap(y)];
 		if (increment_overflow(&y, 1))
-			return NULL;
+			goto overflow;
 	}
 	tmp->tm_year = y;
 	if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE))
-		return NULL;
+		goto overflow;
 	tmp->tm_yday = idays;
 	/*
 	** The "extra" mods below avoid overflow problems.
@@ -1604,6 +1620,8 @@
 	tmp->TM_GMTOFF = offset;
 #endif /* defined TM_GMTOFF */
 	return tmp;
+overflow:
+	return NULL;
 }
 
 char *
@@ -1753,20 +1771,21 @@
 	if (do_norm_secs) {
 		if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
 			SECSPERMIN))
-				return WRONG;
+			goto overflow;
 	}
 	if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
-		return WRONG;
+		goto overflow;
 	if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
-		return WRONG;
+		goto overflow;
 	y = yourtm.tm_year;
 	if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR))
-		return WRONG;
+		goto overflow;
 	/*
 	** Turn y into an actual year number for now.
 	** It is converted back to an offset from TM_YEAR_BASE later.
 	*/
 	if (increment_overflow32(&y, TM_YEAR_BASE))
+		goto overflow;
 		return WRONG;
 	while (yourtm.tm_mday <= 0) {
 		if (increment_overflow32(&y, -1))
@@ -1792,9 +1811,9 @@
 		}
 	}
 	if (increment_overflow32(&y, -TM_YEAR_BASE))
-		return WRONG;
+		goto overflow;
 	if (! (INT_MIN <= y && y <= INT_MAX))
-		return WRONG;
+		goto overflow;
 	yourtm.tm_year = y;
 	if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN)
 		saved_seconds = 0;
@@ -1844,17 +1863,17 @@
 		if (dir != 0) {
 			if (t == lo) {
 				if (t == time_t_max)
-					return WRONG;
+					goto overflow;
 				++t;
 				++lo;
 			} else if (t == hi) {
 				if (t == time_t_min)
-					return WRONG;
+					goto overflow;
 				--t;
 				--hi;
 			}
 			if (lo > hi)
-				return WRONG;
+				goto invalid;
 			if (dir > 0)
 				hi = t;
 			else	lo = t;
@@ -1898,7 +1917,7 @@
 		** gets checked.
 		*/
 		if (sp == NULL)
-			return WRONG;
+			goto invalid;
 		for (i = sp->typecnt - 1; i >= 0; --i) {
 			if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
 				continue;
@@ -1920,16 +1939,22 @@
 				goto label;
 			}
 		}
-		return WRONG;
+		goto invalid;
 	}
 label:
 	newt = t + saved_seconds;
 	if ((newt < t) != (saved_seconds < 0))
-		return WRONG;
+		overflow;
 	t = newt;
 	if (funcp(sp, &t, offset, tmp))
 		*okayp = true;
 	return t;
+invalid:
+	errno = EINVAL;
+	return WRONG;
+overflow:
+	errno = EOVERFLOW;
+	return WRONG;
 }
 
 static time_t
@@ -1966,6 +1991,7 @@
 	char				seen[TZ_MAX_TYPES];
 	unsigned char			types[TZ_MAX_TYPES];
 	bool				okay;
+	int				save_errno;
 
 	if (tmp == NULL) {
 		errno = EINVAL;
@@ -1973,9 +1999,12 @@
 	}
 	if (tmp->tm_isdst > 1)
 		tmp->tm_isdst = 1;
+	save_errno = errno;
 	t = time2(tmp, funcp, sp, offset, &okay);
-	if (okay)
+	if (okay) {
+		errno = save_errno;
 		return t;
+	}
 	if (tmp->tm_isdst < 0)
 #ifdef PCTS
 		/*
@@ -2013,8 +2042,10 @@
 					sp->ttis[samei].tt_gmtoff;
 			tmp->tm_isdst = !tmp->tm_isdst;
 			t = time2(tmp, funcp, sp, offset, &okay);
-			if (okay)
+			if (okay) {
+				errno = save_errno;
 				return t;
+			}
 			tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
 					sp->ttis[samei].tt_gmtoff;
 			tmp->tm_isdst = !tmp->tm_isdst;


More information about the tz mailing list