ce426f
commit 42261ad731991df345880b0b509d83b0b9a9b9d8
ce426f
Author: Florian Weimer <fweimer@redhat.com>
ce426f
Date:   Fri Apr 24 17:34:47 2015 +0200
ce426f
ce426f
    Make time zone file parser more robust [BZ #17715]
ce426f
ce426f
commit 6807b1db8233ed84671f061b5d825622233df303
ce426f
Author: Kevin Easton <kevin@guarana.org>
ce426f
Date:   Tue Feb 24 23:57:07 2015 -0500
ce426f
ce426f
    Reduce lock contention in __tz_convert() [BZ #16145] (partial fix)
ce426f
ce426f
commit 9d46370ca338054cb6ea7ebeddcf06c7ac7ad1a9
ce426f
Author: Joseph Myers <joseph@codesourcery.com>
ce426f
Date:   Fri Oct 16 20:21:49 2015 +0000
ce426f
ce426f
    Convert 703 function definitions to prototype style.
ce426f
    (A subset of these changes (tzset.c) were applied as part of this patch.)
ce426f
ce426f
commit 0748546f660d27a2ad29fa6174d456e2f6490758
ce426f
Author: Paul Eggert <eggert@cs.ucla.edu>
ce426f
Date:   Wed Sep 18 13:15:12 2013 -0700
ce426f
ce426f
    Support TZ transition times < 00:00:00.
ce426f
ce426f
    This is needed for version-3 tz-format files; it supports time
ce426f
    stamps past 2037 for America/Godthab (the only entry in the tz
ce426f
    database for which this change is relevant).
ce426f
    * manual/time.texi (TZ Variable): Document transition times
ce426f
    from -167:59:59 through -00:00:01.
ce426f
    * time/tzset.c (tz_rule): Time of day is now signed.
ce426f
    (__tzset_parse_tz): Parse negative time of day.
ce426f
    (A subset of these changes were applied as part of this patch.)
ce426f
51f0aa
commit 3cc652e951c71785032019fec82e3b8543d85305
51f0aa
Author: Mike Frysinger <vapier@gentoo.org>
51f0aa
Date:   Fri Sep 18 13:49:08 2015 -0400
51f0aa
 
51f0aa
   timezone: fix parallel check failures
51f0aa
   
51f0aa
   The XT testdata install rules expect the testdata dir to already exist in
51f0aa
   the build tree, but it doesn't actually create it.  Instead, it relies on
51f0aa
   the build-testdata define happening to be executed before it (which runs
51f0aa
   zic which creates the dir).  When we run in parallel though, it's easy to
51f0aa
   hit a failure:
51f0aa
   $ cd timezone
51f0aa
   $ rm -rf $objdir/timezone/testdata
51f0aa
   $ make check -j
51f0aa
   ...
51f0aa
   cp testdata/XT1 .../timezone/testdata/XT1
51f0aa
   cp: cannot create regular file '.../timezone/testdata/XT1': No such file or directory
51f0aa
   Makefile:116: recipe for target '.../timezone/testdata/XT1' failed
51f0aa
   make: *** [.../timezone/testdata/XT1] Error 1
51f0aa
   make: *** Waiting for unfinished jobs....
ce426f
ce426f
diff --git a/time/tzfile.c b/time/tzfile.c
ce426f
--- a/time/tzfile.c
ce426f
+++ b/time/tzfile.c
ce426f
@@ -213,6 +213,9 @@
ce426f
   num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt);
ce426f
   num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt);
ce426f
 
ce426f
+  if (__glibc_unlikely (num_isstd > num_types || num_isgmt > num_types))
ce426f
+    goto lose;
ce426f
+
ce426f
   /* For platforms with 64-bit time_t we use the new format if available.  */
ce426f
   if (sizeof (time_t) == 8 && trans_width == 4
ce426f
       && tzhead.tzh_version[0] != '\0')
ce426f
@@ -445,12 +448,20 @@
ce426f
 	goto lose;
ce426f
 
ce426f
       tzspec_len = st.st_size - off - 1;
ce426f
-      char *tzstr = alloca (tzspec_len);
ce426f
+      if (tzspec_len == 0)
ce426f
+        goto lose;
ce426f
+      char *tzstr = malloc (tzspec_len);
ce426f
+      if (tzstr == NULL)
ce426f
+        goto lose;
ce426f
       if (getc_unlocked (f) != '\n'
ce426f
 	  || (fread_unlocked (tzstr, 1, tzspec_len - 1, f) != tzspec_len - 1))
ce426f
-	goto lose;
ce426f
+        {
ce426f
+          free (tzstr);
ce426f
+          goto lose;
ce426f
+        }
ce426f
       tzstr[tzspec_len - 1] = '\0';
ce426f
       tzspec = __tzstring (tzstr);
ce426f
+      free (tzstr);
ce426f
     }
ce426f
 
ce426f
   /* Don't use an empty TZ string.  */
ce426f
ce426f
diff --git a/time/tzset.c b/time/tzset.c
ce426f
--- a/time/tzset.c
ce426f
+++ b/time/tzset.c
ce426f
@@ -1,4 +1,4 @@
ce426f
-/* Copyright (C) 1991-2012 Free Software Foundation, Inc.
ce426f
+/* Copyright (C) 1991-2016 Free Software Foundation, Inc.
ce426f
    This file is part of the GNU C Library.
ce426f
 
ce426f
    The GNU C Library is free software; you can redistribute it and/or
ce426f
@@ -18,6 +18,7 @@
ce426f
 #include <ctype.h>
ce426f
 #include <errno.h>
ce426f
 #include <bits/libc-lock.h>
ce426f
+#include <stdbool.h>
ce426f
 #include <stddef.h>
ce426f
 #include <stdio.h>
ce426f
 #include <stdlib.h>
ce426f
@@ -54,7 +55,7 @@
ce426f
     /* When to change.  */
ce426f
     enum { J0, J1, M } type;	/* Interpretation of:  */
ce426f
     unsigned short int m, n, d;	/* Month, week, day.  */
ce426f
-    unsigned int secs;		/* Time of day.  */
ce426f
+    int secs;			/* Time of day.  */
ce426f
 
ce426f
     long int offset;		/* Seconds east of GMT (west if < 0).  */
ce426f
 
ce426f
@@ -82,15 +83,14 @@
ce426f
 
ce426f
 static struct tzstring_l *tzstring_list;
ce426f
 
ce426f
-/* Allocate a permanent home for S.  It will never be moved or deallocated,
ce426f
-   but may share space with other strings.
ce426f
-   Don't modify the returned string. */
ce426f
-char *
ce426f
-__tzstring (const char *s)
ce426f
+/* Allocate a permanent home for the first LEN characters of S.  It
ce426f
+   will never be moved or deallocated, but may share space with other
ce426f
+   strings.  Don't modify the returned string. */
ce426f
+static char *
ce426f
+__tzstring_len (const char *s, size_t len)
ce426f
 {
ce426f
   char *p;
ce426f
   struct tzstring_l *t, *u, *new;
ce426f
-  size_t len = strlen (s);
ce426f
 
ce426f
   /* Walk the list and look for a match.  If this string is the same
ce426f
      as the end of an already-allocated string, it can share space. */
ce426f
@@ -98,7 +98,7 @@
ce426f
     if (len <= t->len)
ce426f
       {
ce426f
 	p = &t->data[t->len - len];
ce426f
-	if (strcmp (s, p) == 0)
ce426f
+	if (memcmp (s, p, len) == 0)
ce426f
 	  return p;
ce426f
       }
ce426f
 
ce426f
@@ -109,7 +109,8 @@
ce426f
 
ce426f
   new->next = NULL;
ce426f
   new->len = len;
ce426f
-  strcpy (new->data, s);
ce426f
+  memcpy (new->data, s, len);
ce426f
+  new->data[len] = '\0';
ce426f
 
ce426f
   if (u)
ce426f
     u->next = new;
ce426f
@@ -118,6 +119,15 @@
ce426f
 
ce426f
   return new->data;
ce426f
 }
ce426f
+
ce426f
+/* Allocate a permanent home for S.  It will never be moved or
ce426f
+   deallocated, but may share space with other strings.  Don't modify
ce426f
+   the returned string. */
ce426f
+char *
ce426f
+__tzstring (const char *s)
ce426f
+{
ce426f
+  return __tzstring_len (s, strlen (s));
ce426f
+}
ce426f
 
ce426f
 /* Maximum length of a timezone name.  tzset_internal keeps this up to date
ce426f
    (never decreasing it) when ! __use_tzfile.
ce426f
@@ -125,7 +135,7 @@
ce426f
 size_t __tzname_cur_max;
ce426f
 
ce426f
 long int
ce426f
-__tzname_max ()
ce426f
+__tzname_max (void)
ce426f
 {
ce426f
   __libc_lock_lock (tzset_lock);
ce426f
 
ce426f
@@ -164,243 +174,227 @@
ce426f
   return min (ss, 59) + min (mm, 59) * 60 + min (hh, 24) * 60 * 60;
ce426f
 }
ce426f
 
ce426f
-
ce426f
-/* Parse the POSIX TZ-style string.  */
ce426f
-void
ce426f
-__tzset_parse_tz (tz)
ce426f
-     const char *tz;
ce426f
-{
ce426f
-  unsigned short int hh, mm, ss;
ce426f
-
ce426f
-  /* Clear out old state and reset to unnamed UTC.  */
ce426f
-  memset (tz_rules, '\0', sizeof tz_rules);
ce426f
-  tz_rules[0].name = tz_rules[1].name = "";
ce426f
-
ce426f
-  /* Get the standard timezone name.  */
ce426f
-  char *tzbuf = strdupa (tz);
ce426f
-
ce426f
-  int consumed;
ce426f
-  if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1)
ce426f
-    {
ce426f
-      /* Check for the quoted version.  */
ce426f
-      char *wp = tzbuf;
ce426f
-      if (__builtin_expect (*tz++ != '<', 0))
ce426f
-	goto out;
ce426f
-
ce426f
-      while (isalnum (*tz) || *tz == '+' || *tz == '-')
ce426f
-	*wp++ = *tz++;
ce426f
-      if (__builtin_expect (*tz++ != '>' || wp - tzbuf < 3, 0))
ce426f
-	goto out;
ce426f
-      *wp = '\0';
ce426f
+/* Parses the time zone name at *TZP, and writes a pointer to an
ce426f
+   interned string to tz_rules[WHICHRULE].name.  On success, advances
ce426f
+   *TZP, and returns true.  Returns false otherwise.  */
ce426f
+static bool
ce426f
+parse_tzname (const char **tzp, int whichrule)
ce426f
+{
ce426f
+  const char *start = *tzp;
ce426f
+  const char *p = start;
ce426f
+  while (('a' <= *p && *p <= 'z')
ce426f
+	 || ('A' <= *p && *p <= 'Z'))
ce426f
+      ++p;
ce426f
+  size_t len = p - start;
ce426f
+  if (len < 3)
ce426f
+    {
ce426f
+      p = *tzp;
ce426f
+      if (__glibc_unlikely (*p++ != '<'))
ce426f
+	return false;
ce426f
+      start = p;
ce426f
+      while (('a' <= *p && *p <= 'z')
ce426f
+	     || ('A' <= *p && *p <= 'Z')
ce426f
+	     || ('0' <= *p && *p <= '9')
ce426f
+	     || *p == '+' || *p == '-')
ce426f
+	++p;
ce426f
+      len = p - start;
ce426f
+      if (*p++ != '>' || len < 3)
ce426f
+	return false;
ce426f
     }
ce426f
-  else if (__builtin_expect (consumed < 3, 0))
ce426f
-    goto out;
ce426f
-  else
ce426f
-    tz += consumed;
ce426f
 
ce426f
-  tz_rules[0].name = __tzstring (tzbuf);
ce426f
+  tz_rules[whichrule].name = __tzstring_len (start, len);
ce426f
+
ce426f
+  *tzp = p;
ce426f
+  return true;
ce426f
+}
ce426f
 
ce426f
-  /* Figure out the standard offset from UTC.  */
ce426f
-  if (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz)))
ce426f
-    goto out;
ce426f
+/* Parses the time zone offset at *TZP, and writes it to
ce426f
+   tz_rules[WHICHRULE].offset.  Returns true if the parse was
ce426f
+   successful.  */
ce426f
+static bool
ce426f
+parse_offset (const char **tzp, int whichrule)
ce426f
+{
ce426f
+  const char *tz = *tzp;
ce426f
+  if (whichrule == 0
ce426f
+      && (*tz == '\0' || (*tz != '+' && *tz != '-' && !isdigit (*tz))))
ce426f
+    return false;
ce426f
 
ce426f
+  long sign;
ce426f
   if (*tz == '-' || *tz == '+')
ce426f
-    tz_rules[0].offset = *tz++ == '-' ? 1L : -1L;
ce426f
+    sign = *tz++ == '-' ? 1L : -1L;
ce426f
   else
ce426f
-    tz_rules[0].offset = -1L;
ce426f
-  switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
ce426f
-		  &hh, &consumed, &mm, &consumed, &ss, &consumed))
ce426f
-    {
ce426f
-    default:
ce426f
-      tz_rules[0].offset = 0;
ce426f
-      goto out;
ce426f
-    case 1:
ce426f
-      mm = 0;
ce426f
-    case 2:
ce426f
-      ss = 0;
ce426f
-    case 3:
ce426f
-      break;
ce426f
-    }
ce426f
-  tz_rules[0].offset *= compute_offset (ss, mm, hh);
ce426f
-  tz += consumed;
ce426f
-
ce426f
-  /* Get the DST timezone name (if any).  */
ce426f
-  if (*tz != '\0')
ce426f
-    {
ce426f
-      if (sscanf (tz, "%[A-Za-z]%n", tzbuf, &consumed) != 1)
ce426f
-	{
ce426f
-	  /* Check for the quoted version.  */
ce426f
-	  char *wp = tzbuf;
ce426f
-	  const char *rp = tz;
ce426f
-	  if (__builtin_expect (*rp++ != '<', 0))
ce426f
-	    /* Punt on name, set up the offsets.  */
ce426f
-	    goto done_names;
ce426f
-
ce426f
-	  while (isalnum (*rp) || *rp == '+' || *rp == '-')
ce426f
-	    *wp++ = *rp++;
ce426f
-	  if (__builtin_expect (*rp++ != '>' || wp - tzbuf < 3, 0))
ce426f
-	    /* Punt on name, set up the offsets.  */
ce426f
-	    goto done_names;
ce426f
-	  *wp = '\0';
ce426f
-	  tz = rp;
ce426f
-	}
ce426f
-      else if (__builtin_expect (consumed < 3, 0))
ce426f
-	/* Punt on name, set up the offsets.  */
ce426f
-	goto done_names;
ce426f
-      else
ce426f
-	tz += consumed;
ce426f
+    sign = -1L;
ce426f
+  *tzp = tz;
ce426f
 
ce426f
-      tz_rules[1].name = __tzstring (tzbuf);
ce426f
-
ce426f
-      /* Figure out the DST offset from GMT.  */
ce426f
-      if (*tz == '-' || *tz == '+')
ce426f
-	tz_rules[1].offset = *tz++ == '-' ? 1L : -1L;
ce426f
+  unsigned short int hh;
ce426f
+  unsigned short mm = 0;
ce426f
+  unsigned short ss = 0;
ce426f
+  int consumed = 0;
ce426f
+  if (sscanf (tz, "%hu%n:%hu%n:%hu%n",
ce426f
+	      &hh, &consumed, &mm, &consumed, &ss, &consumed) > 0)
ce426f
+    tz_rules[whichrule].offset = sign * compute_offset (ss, mm, hh);
ce426f
+  else
ce426f
+    /* Nothing could be parsed. */
ce426f
+    if (whichrule == 0)
ce426f
+      {
ce426f
+	/* Standard time defaults to offset zero.  */
ce426f
+	tz_rules[0].offset = 0;
ce426f
+	return false;
ce426f
+      }
ce426f
       else
ce426f
-	tz_rules[1].offset = -1L;
ce426f
+	/* DST defaults to one hour later than standard time.  */
ce426f
+	tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
ce426f
+  *tzp = tz + consumed;
ce426f
+  return true;
ce426f
+}
ce426f
 
ce426f
-      switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
ce426f
-		      &hh, &consumed, &mm, &consumed, &ss, &consumed))
ce426f
+/* Parses the standard <-> DST rules at *TZP.  Updates
ce426f
+   tz_rule[WHICHRULE].  On success, advances *TZP and returns true.
ce426f
+   Otherwise, returns false.  */
ce426f
+static bool
ce426f
+parse_rule (const char **tzp, int whichrule)
ce426f
+{
ce426f
+  const char *tz = *tzp;
ce426f
+  tz_rule *tzr = &tz_rules[whichrule];
ce426f
+
ce426f
+  /* Ignore comma to support string following the incorrect
ce426f
+     specification in early POSIX.1 printings.  */
ce426f
+  tz += *tz == ',';
ce426f
+
ce426f
+  /* Get the date of the change.  */
ce426f
+  if (*tz == 'J' || isdigit (*tz))
ce426f
+    {
ce426f
+      char *end;
ce426f
+      tzr->type = *tz == 'J' ? J1 : J0;
ce426f
+      if (tzr->type == J1 && !isdigit (*++tz))
ce426f
+	return false;
ce426f
+      unsigned long int d = strtoul (tz, &end, 10);
ce426f
+      if (end == tz || d > 365)
ce426f
+	return false;
ce426f
+      if (tzr->type == J1 && d == 0)
ce426f
+	return false;
ce426f
+      tzr->d = d;
ce426f
+      tz = end;
ce426f
+    }
ce426f
+  else if (*tz == 'M')
ce426f
+    {
ce426f
+      tzr->type = M;
ce426f
+      int consumed;
ce426f
+      if (sscanf (tz, "M%hu.%hu.%hu%n",
ce426f
+		  &tzr->m, &tzr->n, &tzr->d, &consumed) != 3
ce426f
+	  || tzr->m < 1 || tzr->m > 12
ce426f
+	  || tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
ce426f
+	return false;
ce426f
+      tz += consumed;
ce426f
+    }
ce426f
+  else if (*tz == '\0')
ce426f
+    {
ce426f
+      /* Daylight time rules in the U.S. are defined in the U.S. Code,
ce426f
+	 Title 15, Chapter 6, Subchapter IX - Standard Time.  These
ce426f
+	 dates were established by Congress in the Energy Policy Act
ce426f
+	 of 2005 [Pub. L. no. 109-58, 119 Stat 594 (2005)].
ce426f
+	 Below is the equivalent of "M3.2.0,M11.1.0" [/2 not needed
ce426f
+	 since 2:00AM is the default].  */
ce426f
+      tzr->type = M;
ce426f
+      if (tzr == &tz_rules[0])
ce426f
 	{
ce426f
-	default:
ce426f
-	  /* Default to one hour later than standard time.  */
ce426f
-	  tz_rules[1].offset = tz_rules[0].offset + (60 * 60);
ce426f
-	  break;
ce426f
-
ce426f
-	case 1:
ce426f
-	  mm = 0;
ce426f
-	case 2:
ce426f
-	  ss = 0;
ce426f
-	case 3:
ce426f
-	  tz_rules[1].offset *= compute_offset (ss, mm, hh);
ce426f
-	  tz += consumed;
ce426f
-	  break;
ce426f
+	  tzr->m = 3;
ce426f
+	  tzr->n = 2;
ce426f
+	  tzr->d = 0;
ce426f
 	}
ce426f
-      if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
ce426f
+      else
ce426f
 	{
ce426f
-	  /* There is no rule.  See if there is a default rule file.  */
ce426f
-	  __tzfile_default (tz_rules[0].name, tz_rules[1].name,
ce426f
-			    tz_rules[0].offset, tz_rules[1].offset);
ce426f
-	  if (__use_tzfile)
ce426f
-	    {
ce426f
-	      free (old_tz);
ce426f
-	      old_tz = NULL;
ce426f
-	      return;
ce426f
-	    }
ce426f
+	  tzr->m = 11;
ce426f
+	  tzr->n = 1;
ce426f
+	  tzr->d = 0;
ce426f
 	}
ce426f
     }
ce426f
   else
ce426f
-    {
ce426f
-      /* There is no DST.  */
ce426f
-      tz_rules[1].name = tz_rules[0].name;
ce426f
-      tz_rules[1].offset = tz_rules[0].offset;
ce426f
-      goto out;
ce426f
+    return false;
ce426f
+
ce426f
+  if (*tz != '\0' && *tz != '/' && *tz != ',')
ce426f
+    return false;
ce426f
+  else if (*tz == '/')
ce426f
+    {
ce426f
+      /* Get the time of day of the change.  */
ce426f
+      int negative;
ce426f
+      ++tz;
ce426f
+      if (*tz == '\0')
ce426f
+	return false;
ce426f
+      negative = *tz == '-';
ce426f
+      tz += negative;
ce426f
+      /* Default to 2:00 AM.  */
ce426f
+      unsigned short hh = 2;
ce426f
+      unsigned short mm = 0;
ce426f
+      unsigned short ss = 0;
ce426f
+      int consumed = 0;
ce426f
+      sscanf (tz, "%hu%n:%hu%n:%hu%n",
ce426f
+	      &hh, &consumed, &mm, &consumed, &ss, &consumed);;
ce426f
+      tz += consumed;
ce426f
+      tzr->secs = (negative ? -1 : 1) * ((hh * 60 * 60) + (mm * 60) + ss);
ce426f
     }
ce426f
+  else
ce426f
+    /* Default to 2:00 AM.  */
ce426f
+    tzr->secs = 2 * 60 * 60;
ce426f
 
ce426f
- done_names:
ce426f
-  /* Figure out the standard <-> DST rules.  */
ce426f
-  for (unsigned int whichrule = 0; whichrule < 2; ++whichrule)
ce426f
-    {
ce426f
-      register tz_rule *tzr = &tz_rules[whichrule];
ce426f
+  tzr->computed_for = -1;
ce426f
+  *tzp = tz;
ce426f
+  return true;
ce426f
+}
ce426f
 
ce426f
-      /* Ignore comma to support string following the incorrect
ce426f
-	 specification in early POSIX.1 printings.  */
ce426f
-      tz += *tz == ',';
ce426f
+/* Parse the POSIX TZ-style string.  */
ce426f
+void
ce426f
+__tzset_parse_tz (const char *tz)
ce426f
+{
ce426f
+  /* Clear out old state and reset to unnamed UTC.  */
ce426f
+  memset (tz_rules, '\0', sizeof tz_rules);
ce426f
+  tz_rules[0].name = tz_rules[1].name = "";
ce426f
 
ce426f
-      /* Get the date of the change.  */
ce426f
-      if (*tz == 'J' || isdigit (*tz))
ce426f
-	{
ce426f
-	  char *end;
ce426f
-	  tzr->type = *tz == 'J' ? J1 : J0;
ce426f
-	  if (tzr->type == J1 && !isdigit (*++tz))
ce426f
-	    goto out;
ce426f
-	  unsigned long int d = strtoul (tz, &end, 10);
ce426f
-	  if (end == tz || d > 365)
ce426f
-	    goto out;
ce426f
-	  if (tzr->type == J1 && d == 0)
ce426f
-	    goto out;
ce426f
-	  tzr->d = d;
ce426f
-	  tz = end;
ce426f
-	}
ce426f
-      else if (*tz == 'M')
ce426f
-	{
ce426f
-	  tzr->type = M;
ce426f
-	  if (sscanf (tz, "M%hu.%hu.%hu%n",
ce426f
-		      &tzr->m, &tzr->n, &tzr->d, &consumed) != 3
ce426f
-	      || tzr->m < 1 || tzr->m > 12
ce426f
-	      || tzr->n < 1 || tzr->n > 5 || tzr->d > 6)
ce426f
-	    goto out;
ce426f
-	  tz += consumed;
ce426f
-	}
ce426f
-      else if (*tz == '\0')
ce426f
+  /* Get the standard timezone name.  */
ce426f
+  if (parse_tzname (&tz, 0) && parse_offset (&tz, 0))
ce426f
+    {
ce426f
+      /* Get the DST timezone name (if any).  */
ce426f
+      if (*tz != '\0')
ce426f
 	{
ce426f
-         /* Daylight time rules in the U.S. are defined in the
ce426f
-            U.S. Code, Title 15, Chapter 6, Subchapter IX - Standard
ce426f
-            Time.  These dates were established by Congress in the
ce426f
-            Energy Policy Act of 2005 [Pub. L. no. 109-58, 119 Stat 594
ce426f
-            (2005)].
ce426f
-	    Below is the equivalent of "M3.2.0,M11.1.0" [/2 not needed
ce426f
-	    since 2:00AM is the default].  */
ce426f
-	  tzr->type = M;
ce426f
-	  if (tzr == &tz_rules[0])
ce426f
+	  if (parse_tzname (&tz, 1))
ce426f
 	    {
ce426f
-	      tzr->m = 3;
ce426f
-	      tzr->n = 2;
ce426f
-	      tzr->d = 0;
ce426f
-	    }
ce426f
-	  else
ce426f
-	    {
ce426f
-	      tzr->m = 11;
ce426f
-	      tzr->n = 1;
ce426f
-	      tzr->d = 0;
ce426f
+	      parse_offset (&tz, 1);
ce426f
+	      if (*tz == '\0' || (tz[0] == ',' && tz[1] == '\0'))
ce426f
+		{
ce426f
+		  /* There is no rule.  See if there is a default rule
ce426f
+		     file.  */
ce426f
+		  __tzfile_default (tz_rules[0].name, tz_rules[1].name,
ce426f
+				    tz_rules[0].offset, tz_rules[1].offset);
ce426f
+		  if (__use_tzfile)
ce426f
+		    {
ce426f
+		      free (old_tz);
ce426f
+		      old_tz = NULL;
ce426f
+		      return;
ce426f
+		    }
ce426f
+		}
ce426f
 	    }
ce426f
+	  /* Figure out the standard <-> DST rules.  */
ce426f
+	  if (parse_rule (&tz, 0))
ce426f
+	    parse_rule (&tz, 1);
ce426f
 	}
ce426f
       else
ce426f
-	goto out;
ce426f
-
ce426f
-      if (*tz != '\0' && *tz != '/' && *tz != ',')
ce426f
-	goto out;
ce426f
-      else if (*tz == '/')
ce426f
 	{
ce426f
-	  /* Get the time of day of the change.  */
ce426f
-	  ++tz;
ce426f
-	  if (*tz == '\0')
ce426f
-	    goto out;
ce426f
-	  consumed = 0;
ce426f
-	  switch (sscanf (tz, "%hu%n:%hu%n:%hu%n",
ce426f
-			  &hh, &consumed, &mm, &consumed, &ss, &consumed))
ce426f
-	    {
ce426f
-	    default:
ce426f
-	      hh = 2;		/* Default to 2:00 AM.  */
ce426f
-	    case 1:
ce426f
-	      mm = 0;
ce426f
-	    case 2:
ce426f
-	      ss = 0;
ce426f
-	    case 3:
ce426f
-	      break;
ce426f
-	    }
ce426f
-	  tz += consumed;
ce426f
-	  tzr->secs = (hh * 60 * 60) + (mm * 60) + ss;
ce426f
+	  /* There is no DST.  */
ce426f
+	  tz_rules[1].name = tz_rules[0].name;
ce426f
+	  tz_rules[1].offset = tz_rules[0].offset;
ce426f
 	}
ce426f
-      else
ce426f
-	/* Default to 2:00 AM.  */
ce426f
-	tzr->secs = 2 * 60 * 60;
ce426f
-
ce426f
-      tzr->computed_for = -1;
ce426f
     }
ce426f
 
ce426f
- out:
ce426f
   update_vars ();
ce426f
 }
ce426f
 
ce426f
 /* Interpret the TZ envariable.  */
ce426f
 static void
ce426f
 internal_function
ce426f
-tzset_internal (always, explicit)
ce426f
-     int always;
ce426f
-     int explicit;
ce426f
+tzset_internal (int always, int explicit)
ce426f
 {
ce426f
   static int is_initialized;
ce426f
-  register const char *tz;
ce426f
+  const char *tz;
ce426f
 
ce426f
   if (is_initialized && !always)
ce426f
     return;
ce426f
@@ -467,11 +461,9 @@
ce426f
    put it in RULE->change, saving YEAR in RULE->computed_for.  */
ce426f
 static void
ce426f
 internal_function
ce426f
-compute_change (rule, year)
ce426f
-     tz_rule *rule;
ce426f
-     int year;
ce426f
+compute_change (tz_rule *rule, int year)
ce426f
 {
ce426f
-  register time_t t;
ce426f
+  time_t t;
ce426f
 
ce426f
   if (year != -1 && rule->computed_for == year)
ce426f
     /* Operations on times in 2 BC will be slower.  Oh well.  */
ce426f
@@ -558,10 +550,7 @@
ce426f
    `__timezone', and `__daylight' accordingly.  */
ce426f
 void
ce426f
 internal_function
ce426f
-__tz_compute (timer, tm, use_localtime)
ce426f
-     time_t timer;
ce426f
-     struct tm *tm;
ce426f
-     int use_localtime;
ce426f
+__tz_compute (time_t timer, struct tm *tm, int use_localtime)
ce426f
 {
ce426f
   compute_change (&tz_rules[0], 1900 + tm->tm_year);
ce426f
   compute_change (&tz_rules[1], 1900 + tm->tm_year);
ce426f
@@ -641,6 +630,8 @@
ce426f
       leap_extra_secs = 0;
ce426f
     }
ce426f
 
ce426f
+  __libc_lock_unlock (tzset_lock);
ce426f
+
ce426f
   if (tp)
ce426f
     {
ce426f
       if (! use_localtime)
ce426f
@@ -656,8 +647,6 @@
ce426f
 	tp = NULL;
ce426f
     }
ce426f
 
ce426f
-  __libc_lock_unlock (tzset_lock);
ce426f
-
ce426f
   return tp;
ce426f
 }
ce426f
 
ce426f
diff --git a/timezone/Makefile b/timezone/Makefile
ce426f
index 17424b8..5f18545 100644
ce426f
--- a/timezone/Makefile
ce426f
+++ b/timezone/Makefile
ce426f
@@ -23,7 +23,7 @@
ce426f
 extra-objs := scheck.o ialloc.o
ce426f
 
ce426f
 others	:= zdump zic
ce426f
-tests	:= test-tz tst-timezone
ce426f
+tests	:= test-tz tst-timezone tst-tzset
ce426f
 
ce426f
 # pacificnew doesn't compile; if it is to be used, it should be included in
ce426f
 # northamerica.
ce426f
@@ -87,9 +87,11 @@
ce426f
 				       Australia/Melbourne \
ce426f
 				       America/Sao_Paulo Asia/Tokyo \
ce426f
 				       Europe/London)
ce426f
+$(objpfx)tst-tzset.out: $(addprefix $(testdata)/XT, 1 2 3 4)
ce426f
 
ce426f
 test-tz-ENV = TZDIR=$(testdata)
ce426f
 tst-timezone-ENV = TZDIR=$(testdata)
ce426f
+tst-tzset-ENV = TZDIR=$(testdata)
ce426f
 
ce426f
 # Note this must come second in the deps list for $(built-program-cmd) to work.
ce426f
 zic-deps = $(objpfx)zic $(leapseconds) yearistype
ce426f
@@ -111,6 +113,8 @@
ce426f
 $(testdata)/Asia/Tokyo: asia $(zic-deps)
ce426f
 	$(build-testdata)
ce426f
 
ce426f
+$(testdata)/XT%: testdata/XT%
ce426f
+	cp $< $@
ce426f
 
ce426f
 $(objpfx)tzselect: tzselect.ksh $(common-objpfx)config.make
ce426f
 	sed -e 's|/bin/bash|$(KSH)|g' \
ce426f
diff --git a/timezone/README b/timezone/README
ce426f
index 7a5e31c..2268f8e 100644
ce426f
--- a/timezone/README
ce426f
+++ b/timezone/README
ce426f
@@ -15,3 +15,6 @@ version of the tzcode and tzdata packages.
ce426f
 
ce426f
 These packages may be found at ftp://ftp.iana.org/tz/releases/.  Commentary
ce426f
 should be addressed to tz@iana.org.
ce426f
+
ce426f
+The subdirectory testdata contains manually edited data files for
ce426f
+regression testing purposes.
ce426f
--- /dev/null
ce426f
+++ b/timezone/tst-tzset.c
ce426f
@@ -0,0 +1,200 @@
ce426f
+/* tzset tests with crafted time zone data.
ce426f
+   Copyright (C) 2015 Free Software Foundation, Inc.
ce426f
+
ce426f
+   The GNU C Library is free software; you can redistribute it and/or
ce426f
+   modify it under the terms of the GNU Lesser General Public
ce426f
+   License as published by the Free Software Foundation; either
ce426f
+   version 2.1 of the License, or (at your option) any later version.
ce426f
+
ce426f
+   The GNU C Library is distributed in the hope that it will be useful,
ce426f
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
ce426f
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
ce426f
+   Lesser General Public License for more details.
ce426f
+
ce426f
+   You should have received a copy of the GNU Lesser General Public
ce426f
+   License along with the GNU C Library; if not, see
ce426f
+   <http://www.gnu.org/licenses/>.  */
ce426f
+
ce426f
+#define _GNU_SOURCE 1
ce426f
+
ce426f
+#include <errno.h>
ce426f
+#include <stdio.h>
ce426f
+#include <stdlib.h>
ce426f
+#include <string.h>
ce426f
+#include <sys/resource.h>
ce426f
+#include <time.h>
ce426f
+#include <unistd.h>
ce426f
+
ce426f
+static int do_test (void);
ce426f
+#define TEST_FUNCTION do_test ()
ce426f
+#include "../test-skeleton.c"
ce426f
+
ce426f
+/* Returns the name of a large TZ file.  */
ce426f
+static char *
ce426f
+create_tz_file (off64_t size)
ce426f
+{
ce426f
+  char *path;
ce426f
+  int fd = create_temp_file ("tst-tzset-", &path);
ce426f
+  if (fd < 0)
ce426f
+    exit (1);
ce426f
+
ce426f
+  // Reopen for large-file support.
ce426f
+  close (fd);
ce426f
+  fd = open64 (path, O_WRONLY);
ce426f
+  if (fd < 0)
ce426f
+    {
ce426f
+      printf ("open64 (%s) failed: %m\n", path);
ce426f
+      exit (1);
ce426f
+    }
ce426f
+
ce426f
+  static const char data[] = {
ce426f
+    0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
ce426f
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
ce426f
+    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x58, 0x54, 0x47, 0x00, 0x00, 0x00,
ce426f
+    0x54, 0x5a, 0x69, 0x66, 0x32, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
ce426f
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
ce426f
+    0x00, 0x00, 0x00, 0x04, 0xf8, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
ce426f
+    0x00, 0x00, 0x00, 0x58, 0x54, 0x47, 0x00, 0x00,
ce426f
+    0x00, 0x0a, 0x58, 0x54, 0x47, 0x30, 0x0a
ce426f
+  };
ce426f
+  ssize_t ret = write (fd, data, sizeof (data));
ce426f
+  if (ret < 0)
ce426f
+    {
ce426f
+      printf ("write failed: %m\n");
ce426f
+      exit (1);
ce426f
+    }
ce426f
+  if ((size_t) ret != sizeof (data))
ce426f
+    {
ce426f
+      printf ("Short write\n");
ce426f
+      exit (1);
ce426f
+    }
ce426f
+  if (lseek64 (fd, size, SEEK_CUR) < 0)
ce426f
+    {
ce426f
+      printf ("lseek failed: %m\n");
ce426f
+      close (fd);
ce426f
+      return NULL;
ce426f
+    }
ce426f
+  if (write (fd, "", 1) != 1)
ce426f
+    {
ce426f
+      printf ("Single-byte write failed\n");
ce426f
+      close (fd);
ce426f
+      return NULL;
ce426f
+    }
ce426f
+  if (close (fd) != 0)
ce426f
+    {
ce426f
+      printf ("close failed: %m\n");
ce426f
+      exit (1);
ce426f
+    }
ce426f
+  return path;
ce426f
+}
ce426f
+
ce426f
+static void
ce426f
+test_tz_file (off64_t size)
ce426f
+{
ce426f
+  char *path = create_tz_file (size);
ce426f
+  if (setenv ("TZ", path, 1) < 0)
ce426f
+    {
ce426f
+      printf ("setenv failed: %m\n");
ce426f
+      exit (1);
ce426f
+    }
ce426f
+  tzset ();
ce426f
+  free (path);
ce426f
+}
ce426f
+
ce426f
+static int
ce426f
+do_test (void)
ce426f
+{
ce426f
+  /* Limit the size of the process.  Otherwise, some of the tests will
ce426f
+     consume a lot of resources.  */
ce426f
+  {
ce426f
+    struct rlimit limit;
ce426f
+    if (getrlimit (RLIMIT_AS, &limit) != 0)
ce426f
+      {
ce426f
+	printf ("getrlimit (RLIMIT_AS) failed: %m\n");
ce426f
+	return 1;
ce426f
+      }
ce426f
+    long target = 512 * 1024 * 1024;
ce426f
+    if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
ce426f
+      {
ce426f
+	limit.rlim_cur = 512 * 1024 * 1024;
ce426f
+	if (setrlimit (RLIMIT_AS, &limit) != 0)
ce426f
+	  {
ce426f
+	    printf ("setrlimit (RLIMIT_AS) failed: %m\n");
ce426f
+	    return 1;
ce426f
+	  }
ce426f
+      }
ce426f
+  }
ce426f
+
ce426f
+  int errors = 0;
ce426f
+  for (int i = 1; i <= 4; ++i)
ce426f
+    {
ce426f
+      char tz[16];
ce426f
+      snprintf (tz, sizeof (tz), "XT%d", i);
ce426f
+      if (setenv ("TZ", tz, 1) < 0)
ce426f
+	{
ce426f
+	  printf ("setenv failed: %m\n");
ce426f
+	  return 1;
ce426f
+	}
ce426f
+      tzset ();
ce426f
+      if (strcmp (tzname[0], tz) == 0)
ce426f
+	{
ce426f
+	  printf ("Unexpected success for %s\n", tz);
ce426f
+	  ++errors;
ce426f
+	}
ce426f
+    }
ce426f
+
ce426f
+  /* Large TZ files.  */
ce426f
+
ce426f
+  /* This will succeed on 64-bit architectures, and fail on 32-bit
ce426f
+     architectures.  It used to crash on 32-bit.  */
ce426f
+  test_tz_file (64 * 1024 * 1024);
ce426f
+
ce426f
+  /* This will fail on 64-bit and 32-bit architectures.  It used to
ce426f
+     cause a test timeout on 64-bit and crash on 32-bit if the TZ file
ce426f
+     open succeeded for some reason (it does not use O_LARGEFILE in
ce426f
+     regular builds).  */
ce426f
+  test_tz_file (4LL * 1024 * 1024 * 1024 - 6);
ce426f
+
ce426f
+  /* Large TZ variables.  */
ce426f
+  {
ce426f
+    size_t length = 64 * 1024 * 1024;
ce426f
+    char *value = malloc (length + 1);
ce426f
+    if (value == NULL)
ce426f
+      {
ce426f
+	puts ("malloc failed: %m");
ce426f
+	return 1;
ce426f
+      }
ce426f
+    value[length] = '\0';
ce426f
+
ce426f
+    memset (value, ' ', length);
ce426f
+    value[0] = 'U';
ce426f
+    value[1] = 'T';
ce426f
+    value[2] = 'C';
ce426f
+    if (setenv ("TZ", value, 1) < 0)
ce426f
+      {
ce426f
+	printf ("setenv failed: %m\n");
ce426f
+	return 1;
ce426f
+      }
ce426f
+    tzset ();
ce426f
+
ce426f
+    memset (value, '0', length);
ce426f
+    value[0] = '<';
ce426f
+    value[length - 1] = '>';
ce426f
+    if (setenv ("TZ", value, 1) < 0)
ce426f
+      {
ce426f
+	printf ("setenv failed: %m\n");
ce426f
+	return 1;
ce426f
+      }
ce426f
+    tzset ();
ce426f
+  }
ce426f
+
ce426f
+  return errors > 0;
ce426f
+}
51f0aa
diff --git a/timezone/Makefile b/timezone/Makefile
51f0aa
index 81d4a3e..bfb3463 100644
51f0aa
--- a/timezone/Makefile
51f0aa
+++ b/timezone/Makefile
51f0aa
@@ -113,6 +113,7 @@ $(testdata)/Asia/Tokyo: asia $(zic-deps)
51f0aa
 	$(build-testdata)
51f0aa
 
51f0aa
 $(testdata)/XT%: testdata/XT%
51f0aa
+	$(make-target-directory)
51f0aa
 	cp $< $@
51f0aa
 
51f0aa
 $(objpfx)tzselect: tzselect.ksh $(common-objpfx)config.make
51f0aa
diff -Nrup -a a/timezone/testdata/XT1 b/timezone/testdata/XT1
51f0aa
--- a/timezone/testdata/XT1	1969-12-31 19:00:00.000000000 -0500
51f0aa
+++ b/timezone/testdata/XT1	2017-09-14 10:19:11.382923956 -0400
51f0aa
@@ -0,0 +1,2 @@
51f0aa
+TZif2ÿÿÿÿ???XT1TZif2ÿÿÿÿ????øXT1
51f0aa
+XT10
51f0aa
diff -Nrup -a a/timezone/testdata/XT2 b/timezone/testdata/XT2
51f0aa
--- a/timezone/testdata/XT2	1969-12-31 19:00:00.000000000 -0500
51f0aa
+++ b/timezone/testdata/XT2	2017-09-14 10:19:11.382923956 -0400
51f0aa
@@ -0,0 +1,2 @@
51f0aa
+TZif2?ÿÿÿÿ??XT2TZif2?ÿÿÿÿ???øXT2
51f0aa
+XT20
51f0aa
diff -Nrup -a a/timezone/testdata/XT3 b/timezone/testdata/XT3
51f0aa
--- a/timezone/testdata/XT3	1969-12-31 19:00:00.000000000 -0500
51f0aa
+++ b/timezone/testdata/XT3	2017-09-14 10:19:11.382923956 -0400
51f0aa
@@ -0,0 +1,2 @@
51f0aa
+TZif2????XT3TZif2?????øXT3
51f0aa
+XT30
51f0aa
diff -Nrup -a a/timezone/testdata/XT4 b/timezone/testdata/XT4
51f0aa
--- a/timezone/testdata/XT4	1969-12-31 19:00:00.000000000 -0500
51f0aa
+++ b/timezone/testdata/XT4	2017-09-14 10:19:11.383923953 -0400
51f0aa
@@ -0,0 +1,2 @@
51f0aa
+TZif2????XT4TZif2?????øXT4
51f0aa
+XT40