summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2010-12-07 14:37:50 -0500
committerNick Mathewson <nickm@torproject.org>2011-04-28 17:12:54 -0400
commit51e551d3837990b7c4491253a88bedd2513fe1de (patch)
tree7cf7c7166e57fa8882d8c38caca7226d1c0e1b49
parent3055acbdbe914c31ca4825ed60b4cce6676bd61e (diff)
downloadtor-51e551d3837990b7c4491253a88bedd2513fe1de.tar.gz
tor-51e551d3837990b7c4491253a88bedd2513fe1de.zip
Detect and handle NULL returns from (gm/local)time_r
These functions can return NULL for otherwise-valid values of time_t. Notably, the glibc gmtime manpage says it can return NULL if the year if greater than INT_MAX, and the windows MSDN gmtime page says it can return NULL for negative time_t values. Also, our formatting code is not guaranteed to correctly handle years after 9999 CE. This patch tries to correct this by detecting NULL values from gmtime/localtime_r, and trying to clip them to a reasonable end of the scale. If they are in the middle of the scale, we call it a downright error. Arguably, it's a bug to get out-of-bounds dates like this to begin with. But we've had bugs of this kind in the past, and warning when we see a bug is much kinder than doing a NULL-pointer dereference. Boboper found this one too.
-rw-r--r--changes/gmtime_null6
-rw-r--r--src/common/compat.c104
-rw-r--r--src/common/compat.h9
3 files changed, 98 insertions, 21 deletions
diff --git a/changes/gmtime_null b/changes/gmtime_null
new file mode 100644
index 0000000000..16a25408bf
--- /dev/null
+++ b/changes/gmtime_null
@@ -0,0 +1,6 @@
+ o Minor bugfixes
+ - On some platforms, gmtime and localtime can return NULL under
+ certain circumstances even for well-defined values of time_t.
+ Try to detect and make up for this deficiency. Possible fix for
+ bug 2077. Bugfix on all versions of Tor. Found by boboper.
+
diff --git a/src/common/compat.c b/src/common/compat.c
index 27489e568a..9b7c0b7822 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1996,14 +1996,88 @@ tor_gettimeofday(struct timeval *timeval)
#define TIME_FNS_NEED_LOCKS
#endif
+static struct tm *
+correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
+ struct tm *r)
+{
+ const char *outcome;
+
+ if (PREDICT_LIKELY(r)) {
+ if (r->tm_year > 8099) { /* We can't strftime dates after 9999 CE. */
+ r->tm_year = 8099;
+ r->tm_mon = 11;
+ r->tm_mday = 31;
+ r->tm_yday = 365;
+ r->tm_hour = 23;
+ r->tm_min = 59;
+ r->tm_sec = 59;
+ }
+ return r;
+ }
+
+ /* If we get here, gmtime or localtime returned NULL. It might have done
+ * this because of overrun or underrun, or it might have done it because of
+ * some other weird issue. */
+ if (timep) {
+ if (*timep < 0) {
+ r = resultbuf;
+ r->tm_year = 70; /* 1970 CE */
+ r->tm_mon = 0;
+ r->tm_mday = 1;
+ r->tm_yday = 1;
+ r->tm_hour = 0;
+ r->tm_min = 0 ;
+ r->tm_sec = 0;
+ outcome = "Rounding up to 1970";
+ goto done;
+ } else if (*timep >= INT32_MAX) {
+ /* Rounding down to INT32_MAX isn't so great, but keep in mind that we
+ * only do it if gmtime/localtime tells us NULL. */
+ r = resultbuf;
+ r->tm_year = 137; /* 2037 CE */
+ r->tm_mon = 11;
+ r->tm_mday = 31;
+ r->tm_yday = 365;
+ r->tm_hour = 23;
+ r->tm_min = 59;
+ r->tm_sec = 59;
+ outcome = "Rounding down to 2037";
+ goto done;
+ }
+ }
+
+ /* If we get here, then gmtime/localtime failed without getting an extreme
+ * value for *timep */
+
+ tor_fragile_assert();
+ r = resultbuf;
+ memset(resultbuf, 0, sizeof(struct tm));
+ outcome="can't recover";
+ done:
+ log_warn(LD_BUG, "%s("I64_FORMAT") failed with error %s: %s",
+ islocal?"localtime":"gmtime",
+ timep?I64_PRINTF_ARG(*timep):0,
+ strerror(errno),
+ outcome);
+ return r;
+}
+
+
/** @{ */
/** As localtime_r, but defined for platforms that don't have it:
*
* Convert *<b>timep</b> to a struct tm in local time, and store the value in
* *<b>result</b>. Return the result on success, or NULL on failure.
*/
-#ifndef HAVE_LOCALTIME_R
-#ifdef TIME_FNS_NEED_LOCKS
+#ifdef HAVE_LOCALTIME_R
+struct tm *
+tor_localtime_r(const time_t *timep, struct tm *result)
+{
+ struct tm *r;
+ r = localtime_r(timep, result);
+ return correct_tm(1, timep, result, r);
+}
+#elif defined(TIME_FNS_NEED_LOCKS)
struct tm *
tor_localtime_r(const time_t *timep, struct tm *result)
{
@@ -2013,9 +2087,10 @@ tor_localtime_r(const time_t *timep, struct tm *result)
tor_assert(result);
tor_mutex_acquire(m);
r = localtime(timep);
- memcpy(result, r, sizeof(struct tm));
+ if (r)
+ memcpy(result, r, sizeof(struct tm));
tor_mutex_release(m);
- return result;
+ return correct_tm(1, timep, result, r);
}
#else
struct tm *
@@ -2024,11 +2099,11 @@ tor_localtime_r(const time_t *timep, struct tm *result)
struct tm *r;
tor_assert(result);
r = localtime(timep);
- memcpy(result, r, sizeof(struct tm));
- return result;
+ if (r)
+ memcpy(result, r, sizeof(struct tm));
+ return correct_tm(1, timep, result, r);
}
#endif
-#endif
/** @} */
/** @{ */
@@ -2038,7 +2113,14 @@ tor_localtime_r(const time_t *timep, struct tm *result)
* *<b>result</b>. Return the result on success, or NULL on failure.
*/
#ifndef HAVE_GMTIME_R
-#ifdef TIME_FNS_NEED_LOCKS
+struct tm *
+tor_gmtime_r(const time_t *timep, struct tm *result)
+{
+ struct tm *r;
+ r = gmtime_r(timep, result);
+ return correct_tm(0, timep, result, r);
+}
+#elif defined(TIME_FNS_NEED_LOCKS)
struct tm *
tor_gmtime_r(const time_t *timep, struct tm *result)
{
@@ -2050,7 +2132,7 @@ tor_gmtime_r(const time_t *timep, struct tm *result)
r = gmtime(timep);
memcpy(result, r, sizeof(struct tm));
tor_mutex_release(m);
- return result;
+ return correct_tm(0, timep, result, r);
}
#else
struct tm *
@@ -2060,11 +2142,9 @@ tor_gmtime_r(const time_t *timep, struct tm *result)
tor_assert(result);
r = gmtime(timep);
memcpy(result, r, sizeof(struct tm));
- return result;
+ return correct_tm(0, timep, result, r);
}
#endif
-#endif
-/** @} */
#if defined(USE_WIN32_THREADS)
void
diff --git a/src/common/compat.h b/src/common/compat.h
index 550c08b533..af795ffba9 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -321,17 +321,8 @@ struct timeval {
void tor_gettimeofday(struct timeval *timeval);
-#ifdef HAVE_LOCALTIME_R
-#define tor_localtime_r localtime_r
-#else
struct tm *tor_localtime_r(const time_t *timep, struct tm *result);
-#endif
-
-#ifdef HAVE_GMTIME_R
-#define tor_gmtime_r gmtime_r
-#else
struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
-#endif
#ifndef timeradd
/** Replacement for timeradd on platforms that do not have it: sets tvout to