[tz] Troll throws zic for a loop

Paul Eggert eggert at cs.ucla.edu
Fri Mar 21 03:12:15 UTC 2014


Zefram wrote:
> Attached patch implements.  I didn't touch localtime.c yet.

Thanks for looking into it; that helps quite a bit.

I'm reluctant to have localtime.c invoke malloc, since that'd be a new 
way for localtime to fail; it's purposely designed to not malloc unless 
the builder #defines ALL_STATE.  If mad scientists in Antarctica keep 
cooking up new ways to tell time we may have to do something more 
drastic, but for now bumping TZ_MAX_TIMES a bit should be a relatively 
painless way forward.

The new Antarctica/Troll entry would be rejected by current 'zic' 
implementations, so it may be prudent to comment it out at first, and 
let the new 'zic' propagate for a bit, before uncommenting it in a later 
relese.

As you mention, if Antarctica/Troll is built by a new 'zic', GNU/Linux 
will operate correctly on it but 2014a-or-earlier localtime will 
silently treat it as if it were UTC.  I have verified that Solaris 11 
localtime has similar problems, and I expect other implementations will 
too.  As the issue is limited to one small station in Antarctica I 
suppose it's not a big deal.

For now I installed the attached patches to the experimental version on 
github.  These should fix the bugs mentioned in this area, along with 
some further gotchas that I noticed will reviewing and improving the 
patches.  Further comments welcome.
-------------- next part --------------
From 45badd3cb0a0561ed4ff80d8715dc491f21b2012 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Thu, 20 Mar 2014 13:35:02 -0700
Subject: [PATCH 1/6] Fix overallocation problem in zic.

* zic.c (writezone), NEWS: Give 'writetype' TX_MAX_TYPES entries,
not TZ_MAX_TIMES.  (Thanks to Zefram.)
---
 NEWS  | 5 +++++
 zic.c | 2 +-
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 0214ca3..13b40dc 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,11 @@ Unreleased, experimental changes
     Crimea switches to Moscow time on 2014-03-30 at 02:00 local time.
     (Thanks to Alexander Krivenyshev.)  Move its zone.tab entry from UA to RU.
 
+  Changes affecting 'zic'
+
+    'zic' now is smarter about allocating memory.
+    (Thanks to Andrew Main (Zefram).)
+
   Changes affecting commentary and documentation
 
     Fix Tuesday/Thursday typo in description of time in Israel.
diff --git a/zic.c b/zic.c
index e7b0081..bd388d3 100644
--- a/zic.c
+++ b/zic.c
@@ -1539,7 +1539,7 @@ writezone(const char *const name, const char *const string, char version)
 		register int	thistimei, thistimecnt;
 		register int	thisleapi, thisleapcnt;
 		register int	thistimelim, thisleaplim;
-		int		writetype[TZ_MAX_TIMES];
+		int		writetype[TZ_MAX_TYPES];
 		int		typemap[TZ_MAX_TYPES];
 		register int	thistypecnt;
 		char		thischars[TZ_MAX_CHARS];
-- 
1.8.5.3
-------------- next part --------------
From f3ea6c6f8491d8f6cd1042b7dde356d886462f77 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Thu, 20 Mar 2014 14:34:32 -0700
Subject: [PATCH 2/6] Check for integer overflow errors in zic.

Also, allocate memory faster by growing buffers by a factor of 1.5
each time, rather than simply adding 1 to the size.
* private.h (SIZE_MAX): New macro, for older systems lacking it.
* zic.c (nrules_alloc, nzones_alloc, nlinks_alloc): New static vars.
(memory_exhausted, size_product, growalloc): New functions.
(memcheck, inrule, inzsub, inlink, getfields): Use them.
---
 private.h |  4 ++++
 zic.c     | 49 ++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/private.h b/private.h
index 4eb0ab6..1a85c88 100644
--- a/private.h
+++ b/private.h
@@ -205,6 +205,10 @@ typedef unsigned long uintmax_t;
 #define INT32_MIN (-1 - INT32_MAX)
 #endif /* !defined INT32_MIN */
 
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t) -1)
+#endif
+
 #if 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
 # define ATTRIBUTE_CONST __attribute__ ((const))
 # define ATTRIBUTE_PURE __attribute__ ((__pure__))
diff --git a/zic.c b/zic.c
index bd388d3..d2a846c 100644
--- a/zic.c
+++ b/zic.c
@@ -247,9 +247,11 @@ static int		typecnt;
 
 static struct rule *	rules;
 static int		nrules;	/* number of rules */
+static int		nrules_alloc;
 
 static struct zone *	zones;
 static int		nzones;	/* number of zones */
+static int		nzones_alloc;
 
 struct link {
 	const char *	l_filename;
@@ -260,6 +262,7 @@ struct link {
 
 static struct link *	links;
 static int		nlinks;
+static int		nlinks_alloc;
 
 struct lookup {
 	const char *	l_word;
@@ -361,16 +364,26 @@ static char		roll[TZ_MAX_LEAPS];
 ** Memory allocation.
 */
 
+static _Noreturn void
+memory_exhausted(const char *msg)
+{
+	fprintf(stderr, _("%s: Memory exhausted: %s\n"), progname, msg);
+	exit(EXIT_FAILURE);
+}
+
+static ATTRIBUTE_PURE size_t
+size_product(size_t nitems, size_t itemsize)
+{
+	if (SIZE_MAX / itemsize < nitems)
+		memory_exhausted("size overflow");
+	return nitems * itemsize;
+}
+
 static ATTRIBUTE_PURE void *
 memcheck(void *const ptr)
 {
-	if (ptr == NULL) {
-		const char *e = strerror(errno);
-
-		(void) fprintf(stderr, _("%s: Memory exhausted: %s\n"),
-			progname, e);
-		exit(EXIT_FAILURE);
-	}
+	if (ptr == NULL)
+		memory_exhausted(strerror(errno));
 	return ptr;
 }
 
@@ -379,6 +392,20 @@ memcheck(void *const ptr)
 #define ecpyalloc(ptr)		memcheck(icpyalloc(ptr))
 #define ecatalloc(oldp, newp)	memcheck(icatalloc((oldp), (newp)))
 
+static void *
+growalloc(void *ptr, size_t itemsize, int nitems, int *nitems_alloc)
+{
+	if (nitems < *nitems_alloc)
+		return ptr;
+	else {
+		int amax = INT_MAX < SIZE_MAX ? INT_MAX : SIZE_MAX;
+		if ((amax - 1) / 2 < *nitems_alloc)
+			memory_exhausted("int ooverflow");
+		*nitems_alloc = *nitems_alloc + (*nitems_alloc >> 1) + 1;
+		return erealloc(ptr, size_product(*nitems_alloc, itemsize));
+	}
+}
+
 /*
 ** Error handling.
 */
@@ -965,7 +992,7 @@ inrule(register char **const fields, const int nfields)
 	r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
 	if (max_abbrvar_len < strlen(r.r_abbrvar))
 		max_abbrvar_len = strlen(r.r_abbrvar);
-	rules = erealloc(rules, (nrules + 1) * sizeof *rules);
+	rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
 	rules[nrules++] = r;
 }
 
@@ -1081,7 +1108,7 @@ inzsub(register char **const fields, const int nfields, const int iscont)
 				return FALSE;
 		}
 	}
-	zones = erealloc(zones, (nzones + 1) * sizeof *zones);
+	zones = growalloc(zones, sizeof *zones, nzones, &nzones_alloc);
 	zones[nzones++] = z;
 	/*
 	** If there was an UNTIL field on this line,
@@ -1214,7 +1241,7 @@ inlink(register char **const fields, const int nfields)
 	l.l_linenum = linenum;
 	l.l_from = ecpyalloc(fields[LF_FROM]);
 	l.l_to = ecpyalloc(fields[LF_TO]);
-	links = erealloc(links, (nlinks + 1) * sizeof *links);
+	links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
 	links[nlinks++] = l;
 }
 
@@ -2572,7 +2599,7 @@ getfields(register char *cp)
 
 	if (cp == NULL)
 		return NULL;
-	array = emalloc((strlen(cp) + 1) * sizeof *array);
+	array = emalloc(size_product(strlen(cp) + 1, sizeof *array));
 	nsubs = 0;
 	for ( ; ; ) {
 		while (isascii((unsigned char) *cp) &&
-- 
1.8.5.3
-------------- next part --------------
From 725779ad2ff887e6f137a74e806193fd92615e08 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Thu, 20 Mar 2014 18:11:23 -0700
Subject: [PATCH 3/6] Remove TZ_MAX_TIMES restriction in zic.c.

This lets zic handle the time rules of Troll station,
Antarctica.  Adapted from a fix by Zefram in:
http://mm.icann.org/pipermail/tz/2014-March/020759.html
* zic.c (timecnt_alloc): New static var.
(attypes): Now a pointer to an array, not a fixed size array.
(writezone, addtt): Don't assume the number of times is less than
TZ_MAX_TIMES.
* NEWS: Document this.
---
 NEWS  |  6 ++++--
 zic.c | 14 +++++++-------
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/NEWS b/NEWS
index 13b40dc..351058d 100644
--- a/NEWS
+++ b/NEWS
@@ -10,8 +10,10 @@ Unreleased, experimental changes
 
   Changes affecting 'zic'
 
-    'zic' now is smarter about allocating memory.
-    (Thanks to Andrew Main (Zefram).)
+    'zic' no longer rejects locations needing four transitions per
+    year for the forseeable future, such as Troll station, Antarctica.
+    (Thanks to Andrew Main (Zefram).)  Also, it avoids some unlikely
+    failures due to integer overflow.
 
   Changes affecting commentary and documentation
 
diff --git a/zic.c b/zic.c
index d2a846c..09fda7a 100644
--- a/zic.c
+++ b/zic.c
@@ -162,6 +162,7 @@ static const char *	rfilename;
 static int		rlinenum;
 static const char *	progname;
 static int		timecnt;
+static int		timecnt_alloc;
 static int		typecnt;
 
 /*
@@ -349,7 +350,7 @@ static const int	len_years[2] = {
 static struct attype {
 	zic_t		at;
 	unsigned char	type;
-}			attypes[TZ_MAX_TIMES];
+} *			attypes;
 static zic_t		gmtoffs[TZ_MAX_TYPES];
 static char		isdsts[TZ_MAX_TYPES];
 static unsigned char	abbrinds[TZ_MAX_TYPES];
@@ -1461,8 +1462,9 @@ writezone(const char *const name, const char *const string, char version)
 	static char *			fullname;
 	static const struct tzhead	tzh0;
 	static struct tzhead		tzh;
-	zic_t				ats[TZ_MAX_TIMES];
-	unsigned char			types[TZ_MAX_TIMES];
+	zic_t *ats = emalloc(size_product(timecnt, sizeof *ats + 1));
+	void *typesptr = ats + timecnt;
+	unsigned char *types = typesptr;
 
 	/*
 	** Sort.
@@ -1778,6 +1780,7 @@ writezone(const char *const name, const char *const string, char version)
 			progname, fullname);
 		exit(EXIT_FAILURE);
 	}
+	free(ats);
 }
 
 static void
@@ -2397,10 +2400,7 @@ addtt(const zic_t starttime, int type)
 		timecnt = 0;
 		type = 0;
 	}
-	if (timecnt >= TZ_MAX_TIMES) {
-		error(_("too many transitions?!"));
-		exit(EXIT_FAILURE);
-	}
+	attypes = growalloc(attypes, sizeof *attypes, timecnt, &timecnt_alloc);
 	attypes[timecnt].at = starttime;
 	attypes[timecnt].type = type;
 	++timecnt;
-- 
1.8.5.3
-------------- next part --------------
From a3129e54ee09d5968b62a896717aaa7092cafeee Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Thu, 20 Mar 2014 18:39:12 -0700
Subject: [PATCH 4/6] * tzfile.h (TZ_MAX_TIMES), NEWS: Increase from 1200 to
 2000.

This lets localtime handle Troll station.  Hack suggested by Zefram.
---
 NEWS     | 9 ++++-----
 tzfile.h | 2 +-
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/NEWS b/NEWS
index 351058d..7924e3a 100644
--- a/NEWS
+++ b/NEWS
@@ -8,12 +8,11 @@ Unreleased, experimental changes
     Crimea switches to Moscow time on 2014-03-30 at 02:00 local time.
     (Thanks to Alexander Krivenyshev.)  Move its zone.tab entry from UA to RU.
 
-  Changes affecting 'zic'
+  Changes affecting code
 
-    'zic' no longer rejects locations needing four transitions per
-    year for the forseeable future, such as Troll station, Antarctica.
-    (Thanks to Andrew Main (Zefram).)  Also, it avoids some unlikely
-    failures due to integer overflow.
+    'zic' and 'localtime' no longer reject locations needing four transitions
+    per year for the forseeable future.  (Thanks to Andrew Main (Zefram).)
+    Also, 'zic' avoids some unlikely failures due to integer overflow.
 
   Changes affecting commentary and documentation
 
diff --git a/tzfile.h b/tzfile.h
index 529650d..911130e 100644
--- a/tzfile.h
+++ b/tzfile.h
@@ -97,7 +97,7 @@ struct tzhead {
 */
 
 #ifndef TZ_MAX_TIMES
-#define TZ_MAX_TIMES	1200
+#define TZ_MAX_TIMES	2000
 #endif /* !defined TZ_MAX_TIMES */
 
 #ifndef TZ_MAX_TYPES
-- 
1.8.5.3
-------------- next part --------------
From 440c8dbcca60fe5ab9812425d28251098d4e85ee Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Thu, 20 Mar 2014 18:48:46 -0700
Subject: [PATCH 5/6] * antarctica, NEWS: Add a commented-out entry for Troll
 station.

(Thanks to Paul-Inge Flakstad.)
---
 NEWS       |  5 +++++
 antarctica | 25 +++++++++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/NEWS b/NEWS
index 7924e3a..c33c214 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,11 @@ Unreleased, experimental changes
 
   Changes affecting commentary and documentation
 
+    Add a commented-out entry for Troll station, Antarctica.
+    (Thanks to Paul-Inge Flakstad.)  It requires the 'zic' and 'localtime'
+    fixes mentioned above, so the plan is to wait for a while until these
+    fixes propagate.
+
     Fix Tuesday/Thursday typo in description of time in Israel.
     (Thanks to Bert Katz via Pavel Kharitonov and Mike Frysinger.)
 
diff --git a/antarctica b/antarctica
index 5333b7b..2e0806c 100644
--- a/antarctica
+++ b/antarctica
@@ -248,6 +248,31 @@ Rule	NZAQ	2008	max	-	Apr	Sun>=1	2:00s	0	S
 #
 # claims
 # Peter I Island (never inhabited)
+#
+# year-round base
+# Troll, Jutulsessen, -720041+0023206, since 2005-02-11
+#
+# From Paul-Inge Flakstad (2014-03-10):
+# I recently had a long dialog about this with the developer of timegenie.com.
+# In the absence of specific dates, he decided to choose some likely ones:
+#   GMT +1 — From March 1 to the last Sunday in March
+#   GMT +2 — From the last Sunday in March until the last Sunday in October
+#   GMT +1 — From the last Sunday in October until November 7
+#   GMT +0 — From November 7 until March 1
+# The dates for switching to and from UTC+0 will probably not be absolutely
+# correct, but they should be quite close to the actual dates.
+#
+# From Paul Eggert (2014-03-20):
+# This entry requires tzcode 2014b or later, so comment it out for now.
+# Uncomment it when 2014b is supported in more places.
+## Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
+#Rule	Troll	2005	max	-	Mar	 1	1:00u	1:00	CET
+#Rule	Troll	2005	max	-	Mar	lastSun	1:00u	2:00	CEST
+#Rule	Troll	2005	max	-	Oct	lastSun	1:00u	1:00	CET
+#Rule	Troll	2004	max	-	Nov	 7	1:00u	0:00	UTC
+## Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
+#Zone Antarctica/Troll	0	-	zzz	2005 Feb 11
+#     			0:00	Troll	%s
 
 # Poland - year-round base
 # Arctowski, King George Island, -620945-0582745, since 1977
-- 
1.8.5.3
-------------- next part --------------
From b979bceed1f14619df7b3331e1a7247dcbe87815 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert at cs.ucla.edu>
Date: Thu, 20 Mar 2014 19:01:21 -0700
Subject: [PATCH 6/6] Remove unused rules, and add a check for this.

* checktab.awk: Check for a Rule defined but never used.
* antarctica (NZAQ): Remove unused rule.
* NEWS: Document this.
---
 NEWS         |  5 +++++
 antarctica   | 12 ------------
 checktab.awk | 20 +++++++++++++++++---
 3 files changed, 22 insertions(+), 15 deletions(-)

diff --git a/NEWS b/NEWS
index c33c214..4b53037 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,11 @@ Unreleased, experimental changes
     per year for the forseeable future.  (Thanks to Andrew Main (Zefram).)
     Also, 'zic' avoids some unlikely failures due to integer overflow.
 
+  Changes affecting build procedure
+
+    'make check' now detects Rule lines defined but never used.
+    The NZAQ rules, an instance of this problem, have been removed.
+
   Changes affecting commentary and documentation
 
     Add a commented-out entry for Troll station, Antarctica.
diff --git a/antarctica b/antarctica
index 2e0806c..06da875 100644
--- a/antarctica
+++ b/antarctica
@@ -230,18 +230,6 @@ Zone Antarctica/Syowa	0	-	zzz	1957 Jan 29
 # year-round base
 # Scott Base, Ross Island, since 1957-01.
 # See Pacific/Auckland.
-#
-# These rules for New Zealand are stolen from the 'australasia' file.
-# Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
-Rule	NZAQ	1974	only	-	Nov	 3	2:00s	1:00	D
-Rule	NZAQ	1975	1988	-	Oct	lastSun	2:00s	1:00	D
-Rule	NZAQ	1989	only	-	Oct	 8	2:00s	1:00	D
-Rule	NZAQ	1990	2006	-	Oct	Sun>=1	2:00s	1:00	D
-Rule	NZAQ	1975	only	-	Feb	23	2:00s	0	S
-Rule	NZAQ	1976	1989	-	Mar	Sun>=1	2:00s	0	S
-Rule	NZAQ	1990	2007	-	Mar	Sun>=15	2:00s	0	S
-Rule	NZAQ	2007	max	-	Sep	lastSun	2:00s	1:00	D
-Rule	NZAQ	2008	max	-	Apr	Sun>=1	2:00s	0	S
 
 # Norway - territories
 # Bouvet (never inhabited)
diff --git a/checktab.awk b/checktab.awk
index fec4f62..d78ba73 100644
--- a/checktab.awk
+++ b/checktab.awk
@@ -115,10 +115,14 @@ BEGIN {
 	FS = " "
 }
 
+$1 ~ /^#/ { next }
+
 {
-	tz = ""
-	if ($1 == "Zone") tz = $2
-	if ($1 == "Link") {
+	tz = rules = ""
+	if ($1 == "Zone") {
+		tz = $2
+		ruleUsed[$4] = 1
+	} else if ($1 == "Link") {
 		# Ignore Link commands if source and destination basenames
 		# are identical, e.g. Europe/Istanbul versus Asia/Istanbul.
 		src = $2
@@ -126,6 +130,10 @@ BEGIN {
 		while ((i = index(src, "/"))) src = substr(src, i+1)
 		while ((i = index(dst, "/"))) dst = substr(dst, i+1)
 		if (src != dst) tz = $3
+	} else if ($1 == "Rule") {
+		ruleDefined[$2] = 1
+	} else {
+		ruleUsed[$2] = 1
 	}
 	if (tz && tz ~ /\//) {
 		if (!tztab[tz]) {
@@ -138,6 +146,12 @@ BEGIN {
 }
 
 END {
+	for (tz in ruleDefined) {
+		if (!ruleUsed[tz]) {
+			printf "%s: Rule never used\n", tz
+			status = 1
+		}
+	}
 	for (tz in tz2cc) {
 		if (!zoneSeen[tz]) {
 			printf "%s:%d: no Zone table for `%s'\n", \
-- 
1.8.5.3


More information about the tz mailing list