bug introduced in tzcode2005c

Todd C. Miller Todd.Miller at courtesan.com
Tue Aug 9 03:28:19 UTC 2005


A change in tzcode 2005c causes the following program on OpenBSD
to loop forever.  I've tracked it down to the change included at
the end of this message.

Previously::

$ ./mktime 
Timestamp is: -1
Date is: Thu Jan  1 00:59:59 1970                                               

With tzcode 2005c and beyond it loops forever.

#include <sys/types.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>

/*
 * This small programm will consume 100% CPU and loops forever with
 * tzcode 2005c - 2005k.
 */
int
main(int argc, char **argv)
{
        time_t timet;
        struct tm tm;

        tm.tm_sec = 36;
        tm.tm_min = 5;
        tm.tm_hour = 23;
        tm.tm_mday = 31;
        tm.tm_mon = 11;
        tm.tm_year = 138;       /* 2038 - 1900 */
        tm.tm_isdst = 0;

        timet = mktime(&tm);
        printf("Timestamp is: %d\n", timet);
        printf("Date is: %s\n", ctime(&timet));
        exit(0);
}

Here's the change that triggers it.

--- tzcode2005b/localtime.c	Mon Jan 10 07:19:41 2005
+++ tzcode2005c/localtime.c	Mon Jan 17 16:36:15 2005
@@ -1441,10 +1436,11 @@
 {
 	register const struct state *	sp;
 	register int			dir;
-	register int			bits;
 	register int			i, j;
 	register int			saved_seconds;
 	register long			li;
+	register time_t			lo;
+	register time_t			hi;
 	long				y;
 	time_t				newt;
 	time_t				t;
@@ -1518,28 +1514,49 @@
 		yourtm.tm_sec = 0;
 	}
 	/*
-	** Divide the search space in half
-	** (this works whether time_t is signed or unsigned).
+	** Do a binary search (this works whatever time_t's type is).
 	*/
-	bits = TYPE_BIT(time_t) - 1;
-	/*
-	** If time_t is signed, then 0 is just above the median,
-	** assuming two's complement arithmetic.
-	** If time_t is unsigned, then (1 << bits) is just above the median.
-	*/
-	t = TYPE_SIGNED(time_t) ? 0 : (((unsigned long) 1) << bits);
+	if (!TYPE_SIGNED(time_t)) {
+		lo = 0;
+		hi = lo - 1;
+	} else if (!TYPE_INTEGRAL(time_t)) {
+		if (sizeof(time_t) > sizeof(float))
+			hi = (time_t) DBL_MAX;
+		else	hi = (time_t) FLT_MAX;
+		lo = -hi;
+	} else {
+		lo = 1;
+		for (i = 0; i < (int) TYPE_BIT(time_t) - 1; ++i)
+			lo *= 2;
+		hi = -(lo + 1);
+	}
 	for ( ; ; ) {
-		if ((*funcp)(&t, offset, &mytm) == NULL)
-			return WRONG;	/* XXX probably wrong */
-		dir = tmcomp(&mytm, &yourtm);
+		t = lo / 2 + hi / 2;
+		if (t < lo)
+			t = lo;
+		else if (t > hi)
+			t = hi;
+		if ((*funcp)(&t, offset, &mytm) == NULL) {
+			/*
+			** Assume that t is too extreme to be represented in
+			** a struct tm; arrange things so that it is less
+			** extreme on the next pass.
+			*/
+			dir = (t > 0) ? 1 : -1;
+		} else	dir = tmcomp(&mytm, &yourtm);
 		if (dir != 0) {
-			if (bits-- < 0)
+			if (t == lo) {
+				++t;
+				++lo;
+			} else if (t == hi) {
+				--t;
+				--hi;
+			}
+			if (lo > hi)
 				return WRONG;
-			if (bits < 0)
-				--t; /* may be needed if new t is minimal */
-			else if (dir > 0)
-				t -= ((long) 1) << bits;
-			else	t += ((long) 1) << bits;
+			if (dir > 0)
+				hi = t;
+			else	lo = t;
 			continue;
 		}
 		if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)



More information about the tz mailing list