diff options
-rw-r--r-- | changes/gmtime_null | 6 | ||||
-rw-r--r-- | src/common/compat.c | 112 | ||||
-rw-r--r-- | src/common/compat.h | 9 |
3 files changed, 103 insertions, 24 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 4537380dd8..7b2b2fbadb 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -2097,14 +2097,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) { @@ -2114,9 +2188,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 * @@ -2125,11 +2200,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 /** @} */ /** @{ */ @@ -2138,8 +2213,15 @@ tor_localtime_r(const time_t *timep, struct tm *result) * Convert *<b>timep</b> to a struct tm in UTC, and store the value in * *<b>result</b>. Return the result on success, or NULL on failure. */ -#ifndef HAVE_GMTIME_R -#ifdef TIME_FNS_NEED_LOCKS +#ifdef HAVE_GMTIME_R +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) { @@ -2149,9 +2231,10 @@ tor_gmtime_r(const time_t *timep, struct tm *result) tor_assert(result); tor_mutex_acquire(m); r = gmtime(timep); - memcpy(result, r, sizeof(struct tm)); + if (r) + memcpy(result, r, sizeof(struct tm)); tor_mutex_release(m); - return result; + return correct_tm(0, timep, result, r); } #else struct tm * @@ -2160,12 +2243,11 @@ tor_gmtime_r(const time_t *timep, struct tm *result) struct tm *r; tor_assert(result); r = gmtime(timep); - memcpy(result, r, sizeof(struct tm)); - return result; + if (r) + memcpy(result, r, sizeof(struct tm)); + 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 6d2565ac19..b658ed51a0 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -323,17 +323,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 |