diff --git a/ChangeLog b/ChangeLog index b63951bcd0..a8a24d6216 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Tue Apr 6 21:55:25 2010 Tanaka Akira + + * configure.in: test localtime(3) overflow. [ruby-dev:40910] + + * time.c (rb_gmtime_r): renamed from rb_gmtime. + (rb_localtime_r): renamed from rb_localtime. + (rb_localtime_r2): call rb_localtime_r and validate the result if + there is overflow problem. + (rb_gmtime_r2): call rb_gmtime_r and validate the result if there + is overflow problem. + Tue Apr 6 14:53:17 2010 NAKAMURA Usaku * include/ruby/win32.h: check definition existance before defining diff --git a/configure.in b/configure.in index 5047e9e054..27fbe56d98 100644 --- a/configure.in +++ b/configure.in @@ -1290,6 +1290,48 @@ if test "$rb_cv_negative_time_t" = yes; then AC_DEFINE(NEGATIVE_TIME_T) fi +# [ruby-dev:40910] overflow of time on FreeBSD +# http://www.freebsd.org/cgi/query-pr.cgi?pr=145341 +AC_CACHE_CHECK(for localtime(3) overflow correctly, rb_cv_localtime_overflow, + [AC_TRY_RUN([ +#include + +void +check(time_t t1) +{ + struct tm *tm; + time_t t2; + tm = localtime(&t1); + if (!tm) + return; /* overflow detected. ok. */ + t2 = mktime(tm); + if (t1 == t2) + return; /* round-trip. ok. */ + exit(1); +} + +int +main() +{ + time_t t; + if (~(time_t)0 <= 0) { + t = (((time_t)1) << (sizeof(time_t) * 8 - 2)); + t |= t - 1; + } + else { + t = ~(time_t)0; + } + check(t); + return 0; +} +], + rb_cv_localtime_overflow=yes, + rb_cv_localtime_overflow=no, + rb_cv_localtime_overflow=yes)]) +if test "$rb_cv_localtime_overflow" = no; then + AC_DEFINE(LOCALTIME_OVERFLOW_PROBLEM) +fi + if test "$ac_cv_func_sigprocmask" = yes && test "$ac_cv_func_sigaction" = yes; then AC_DEFINE(POSIX_SIGNAL) else diff --git a/time.c b/time.c index d43165ec55..08d4199fdb 100644 --- a/time.c +++ b/time.c @@ -840,31 +840,62 @@ static int leap_year_p(long y); #define leap_year_v_p(y) leap_year_p(NUM2LONG(mod(v, INT2FIX(400)))) #ifdef HAVE_GMTIME_R -#define IF_HAVE_GMTIME_R(x) x -#define ASCTIME(tm, buf) asctime_r((tm), (buf)) -#define GMTIME(tm, result) gmtime_r((tm), &(result)) -#define LOCALTIME(tm, result) (tzset(),localtime_r((tm), &(result))) +#define rb_gmtime_r(t, tm) gmtime_r(t, tm) +#define rb_localtime_r(t, tm) localtime_r(t, tm) +#else +static inline struct tm * +rb_gmtime_r(const time_t *tp, struct tm *result) +{ + struct tm *t = gmtime(tp); + if (t) *result = *t; + return t; +} + +static inline struct tm * +rb_localtime_r(const time_t *tp, struct tm *result) +{ + struct tm *t = localtime(tp); + if (t) *result = *t; + return t; +} +#endif + +static struct tm * +rb_localtime_r2(const time_t *t, struct tm *result) +{ + result = rb_localtime_r(t, result); +#if defined(HAVE_MKTIME) && defined(LOCALTIME_OVERFLOW_PROBLEM) + if (result) { + time_t t2 = mktime(result); + if (*t != t2) + result = NULL; + } +#endif + return result; +} +#define LOCALTIME(tm, result) (tzset(),rb_localtime_r2((tm), &(result))) + +#if !defined(HAVE_STRUCT_TM_TM_GMTOFF) + static struct tm * + rb_gmtime_r2(const time_t *t, struct tm *result) + { + result = rb_gmtime_r(t, result); +#if defined(HAVE_TIMEGM) && defined(LOCALTIME_OVERFLOW_PROBLEM) + if (result) { + time_t t2 = timegm(result); + if (*t != t2) + result = NULL; + } +#endif + return result; + } +# define GMTIME(tm, result) rb_gmtime_r2((tm), &(result)) +#endif + +#ifdef HAVE_GMTIME_R +#define ASCTIME(tm, buf) asctime_r((tm), (buf)) #else -#define IF_HAVE_GMTIME_R(x) /* nothing */ #define ASCTIME(tm, buf) asctime(tm) -#define GMTIME(tm, result) rb_gmtime((tm), &(result)) -#define LOCALTIME(tm, result) rb_localtime((tm), &(result)) - -static inline struct tm * -rb_gmtime(const time_t *tm, struct tm *result) -{ - struct tm *t = gmtime(tm); - if (t) *result = *t; - return t; -} - -static inline struct tm * -rb_localtime(const time_t *tm, struct tm *result) -{ - struct tm *t = localtime(tm); - if (t) *result = *t; - return t; -} #endif static const int common_year_yday_offset[] = { @@ -1085,7 +1116,7 @@ gmtime_with_leapsecond(const time_t *timep, struct tm *result) int sign; int gmtoff_sec, gmtoff_min, gmtoff_hour, gmtoff_day; long gmtoff; - t = localtime_r(timep, result); + t = LOCALTIME(timep, *result); if (t == NULL) return NULL;