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