Bug 480740: When Visual C++ 2005 is used, don't call mktime when we know NSPR_4_7_BRANCH
authorwtc%google.com
Tue, 24 Mar 2009 22:58:57 +0000
branchNSPR_4_7_BRANCH
changeset 4085 86415c862e063d250bcd8e42155b6568bb7dcedf
parent 4084 4417a55f7e1bc0f5e2b3c91627cf6e52e205b58b
child 4087 679029abba9ce2447351aaebae26fb86b48905c3
push idunknown
push userunknown
push dateunknown
bugs480740
Bug 480740: When Visual C++ 2005 is used, don't call mktime when we know the input date would cause mktime to call the invalid parameter handler. r=nelson. Modified Files: Tag: NSPR_4_7_BRANCH pr/src/misc/prtime.c pr/tests/Makefile.in pr/tests/runtests.pl pr/tests/runtests.sh Added Files: Tag: NSPR_4_7_BRANCH pr/tests/parsetm.c
pr/src/misc/prtime.c
pr/tests/Makefile.in
pr/tests/parsetm.c
pr/tests/runtests.pl
pr/tests/runtests.sh
--- a/pr/src/misc/prtime.c
+++ b/pr/src/misc/prtime.c
@@ -45,20 +45,18 @@
 #include "prinit.h"
 #include "prtime.h"
 #include "prlock.h"
 #include "prprf.h"
 #include "prlog.h"
 
 #include <string.h>
 #include <ctype.h>
-
-#ifdef XP_MAC
+#include <errno.h>  /* for EINVAL */
 #include <time.h>
-#endif
 
 /* 
  * The COUNT_LEAPS macro counts the number of leap years passed by
  * till the start of the given year Y.  At the start of the year 4
  * A.D. the number of leap years passed by is 0, while at the start of
  * the year 5 A.D. this count is 1. The number of years divisible by
  * 100 but not divisible by 400 (the non-leap years) is deducted from
  * the count to get the correct number of leap years.
@@ -514,18 +512,16 @@ PR_NormalizeTime(PRExplodedTime *time, P
  *     The following uses localtime() from the standard C library.
  *     (time.h)  This is our fallback implementation.  Unix and PC
  *     use this version.  Mac has its own machine-dependent
  *     implementation of this function.
  *
  *-------------------------------------------------------------------------
  */
 
-#include <time.h>
-
 #if defined(HAVE_INT_LOCALTIME_R)
 
 /*
  * In this case we could define the macro as
  *     #define MT_safe_localtime(timer, result) \
  *             (localtime_r(timer, result) == 0 ? result : NULL)
  * I chose to compare the return value of localtime_r with -1 so 
  * that I can catch the cases where localtime_r returns a pointer
@@ -1568,16 +1564,22 @@ PR_ParseTimeStringToExplodedTime(
   if (date != -1)
         result->tm_mday = date;
   if (month != TT_UNKNOWN)
         result->tm_month = (((int)month) - ((int)TT_JAN));
   if (year != -1)
         result->tm_year = year;
   if (dotw != TT_UNKNOWN)
         result->tm_wday = (((int)dotw) - ((int)TT_SUN));
+  /*
+   * Mainly to compute wday and yday, but normalized time is also required
+   * by the check below that works around a Visual C++ 2005 mktime problem.
+   */
+  PR_NormalizeTime(result, PR_GMTParameters);
+  /* The remaining work is to set the gmt and dst offsets in tm_params. */
 
   if (zone == TT_UNKNOWN && default_to_gmt)
         {
           /* No zone was specified, so pretend the zone was GMT. */
           zone = TT_GMT;
           zone_offset = 0;
         }
 
@@ -1617,17 +1619,42 @@ PR_ParseTimeStringToExplodedTime(
                   localTime.tm_mday = result->tm_mday;
                   localTime.tm_mon = result->tm_month;
                   localTime.tm_year = result->tm_year - 1900;
                   /* Set this to -1 to tell mktime "I don't care".  If you set
                      it to 0 or 1, you are making assertions about whether the
                      date you are handing it is in daylight savings mode or not;
                      and if you're wrong, it will "fix" it for you. */
                   localTime.tm_isdst = -1;
+
+#if _MSC_VER == 1400  /* 1400 = Visual C++ 2005 (8.0) */
+                  /*
+                   * mktime will return (time_t) -1 if the input is a date
+                   * after 23:59:59, December 31, 3000, US Pacific Time (not
+                   * UTC as documented): 
+                   * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx
+                   * But if the year is 3001, mktime also invokes the invalid
+                   * parameter handler, causing the application to crash.  This
+                   * problem has been reported in
+                   * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036.
+                   * We avoid this crash by not calling mktime if the date is
+                   * out of range.  To use a simple test that works in any time
+                   * zone, we consider year 3000 out of range as well.  (See
+                   * bug 480740.)
+                   */
+                  if (result->tm_year >= 3000) {
+                      /* Emulate what mktime would have done. */
+                      errno = EINVAL;
+                      secs = (time_t) -1;
+                  } else {
+                      secs = mktime(&localTime);
+                  }
+#else
                   secs = mktime(&localTime);
+#endif
                   if (secs != (time_t) -1)
                     {
                       PRTime usecs64;
                       LL_I2L(usecs64, secs);
                       LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
                       LL_MUL(usecs64, usecs64, usec_per_sec);
                       PR_ExplodeTime(usecs64, PR_LocalTimeParameters, result);
                       return PR_SUCCESS;
@@ -1639,18 +1666,16 @@ PR_ParseTimeStringToExplodedTime(
                    the zone offset on 00:00:00 2 Jan 1970 GMT. */
                 secs = 86400;
                 (void) MT_safe_localtime(&secs, &localTime);
                 zone_offset = localTime.tm_min
                               + 60 * localTime.tm_hour
                               + 1440 * (localTime.tm_mday - 2);
         }
 
-  /* mainly to compute wday and yday */
-  PR_NormalizeTime(result, PR_GMTParameters);
   result->tm_params.tp_gmt_offset = zone_offset * 60;
   result->tm_params.tp_dst_offset = dst_offset * 60;
 
   return PR_SUCCESS;
 }
 
 PR_IMPLEMENT(PRStatus)
 PR_ParseTimeString(
--- a/pr/tests/Makefile.in
+++ b/pr/tests/Makefile.in
@@ -120,16 +120,17 @@ CSRCS =             \
 	op_2long.c      \
 	op_excl.c		\
 	op_filnf.c		\
 	op_filok.c		\
 	op_noacc.c		\
 	op_nofil.c		\
 	openfile.c		\
 	parent.c    	\
+	parsetm.c    	\
 	peek.c    		\
 	perf.c    		\
 	pipeping.c		\
 	pipeping2.c		\
 	pipepong.c		\
 	pipepong2.c		\
 	pipeself.c		\
 	poll_er.c		\
new file mode 100644
--- /dev/null
+++ b/pr/tests/parsetm.c
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * This test program should eventually become a full-blown test for
+ * PR_ParseTimeString.  Right now it just verifies that PR_ParseTimeString
+ * doesn't crash on an out-of-range time string (bug 480740).
+ */
+
+#include "prtime.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+PRBool debug_mode = PR_TRUE;
+
+static char *dayOfWeek[] =
+	{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "???" };
+static char *month[] =
+	{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" };
+
+static void PrintExplodedTime(const PRExplodedTime *et) {
+    PRInt32 totalOffset;
+    PRInt32 hourOffset, minOffset;
+    const char *sign;
+
+    /* Print day of the week, month, day, hour, minute, and second */
+    if (debug_mode) printf("%s %s %ld %02ld:%02ld:%02ld ",
+	    dayOfWeek[et->tm_wday], month[et->tm_month], et->tm_mday,
+	    et->tm_hour, et->tm_min, et->tm_sec);
+
+    /* Print time zone */
+    totalOffset = et->tm_params.tp_gmt_offset + et->tm_params.tp_dst_offset;
+    if (totalOffset == 0) {
+	if (debug_mode) printf("UTC ");
+    } else {
+        sign = "+";
+        if (totalOffset < 0) {
+	    totalOffset = -totalOffset;
+	    sign = "-";
+        }
+        hourOffset = totalOffset / 3600;
+        minOffset = (totalOffset % 3600) / 60;
+        if (debug_mode) 
+            printf("%s%02ld%02ld ", sign, hourOffset, minOffset);
+    }
+
+    /* Print year */
+    if (debug_mode) printf("%hd", et->tm_year);
+}
+
+int main(int argc, char **argv)
+{
+    PRTime ct;
+    PRExplodedTime et;
+    PRStatus rv;
+    char *sp1 = "Sat, 1 Jan 3001 00:00:00";  /* no time zone */
+    char *sp2 = "Fri, 31 Dec 3000 23:59:60";  /* no time zone, not normalized */
+
+#if _MSC_VER >= 1400
+    /* Run this test in the US Pacific Time timezone. */
+    _putenv_s("TZ", "PST8PDT");
+    _tzset();
+#endif
+
+    rv = PR_ParseTimeString(sp1, PR_FALSE, &ct);
+    printf("rv = %d\n", rv);
+    PR_ExplodeTime(ct, PR_GMTParameters, &et);
+    PrintExplodedTime(&et);
+    printf("\n");
+
+    rv = PR_ParseTimeString(sp2, PR_FALSE, &ct);
+    printf("rv = %d\n", rv);
+    PR_ExplodeTime(ct, PR_GMTParameters, &et);
+    PrintExplodedTime(&et);
+    printf("\n");
+
+    return 0;
+}
--- a/pr/tests/runtests.pl
+++ b/pr/tests/runtests.pl
@@ -328,16 +328,17 @@ sub win_test_prog {
 "ntioto",
 "ntoh",
 "op_2long",
 "op_excl",
 "op_filnf",
 "op_filok",
 "op_nofil",
 "parent",
+"parsetm",
 "peek",
 "perf",
 "pipeping",
 "pipeping2",
 "pipeself",
 "poll_nm",
 "poll_to",
 "pollable",
--- a/pr/tests/runtests.sh
+++ b/pr/tests/runtests.sh
@@ -156,16 +156,17 @@ nonblock
 ntioto
 ntoh
 op_2long
 op_excl
 op_filnf
 op_filok
 op_nofil
 parent
+parsetm
 peek
 perf
 pipeping
 pipeping2
 pipeself
 poll_nm
 poll_to
 pollable