Loading changes/bug40383 0 → 100644 +7 −0 Original line number Original line Diff line number Diff line o Minor bugfixes (timekeeping): - Calculate the time of day correctly on systems where the time_t type includes leap seconds. (This is not the case on most operating systems, but on those where it occurs, our tor_timegm function did not correctly invert the system's gmtime function, which could result in assertion failures when calculating voting schedules.) Fixes bug 40383; bugfix on 0.2.0.3-alpha. configure.ac +1 −0 Original line number Original line Diff line number Diff line Loading @@ -806,6 +806,7 @@ AC_CHECK_FUNCS( strtoull \ strtoull \ sysconf \ sysconf \ sysctl \ sysctl \ timegm \ truncate \ truncate \ uname \ uname \ usleep \ usleep \ Loading src/lib/encoding/time_fmt.c +33 −2 Original line number Original line Diff line number Diff line Loading @@ -13,6 +13,7 @@ * and handles a larger variety of types. It converts between different time * and handles a larger variety of types. It converts between different time * formats, and encodes and decodes them from strings. * formats, and encodes and decodes them from strings. **/ **/ #define TIME_FMT_PRIVATE #include "lib/encoding/time_fmt.h" #include "lib/encoding/time_fmt.h" #include "lib/log/log.h" #include "lib/log/log.h" Loading @@ -25,6 +26,7 @@ #include <string.h> #include <string.h> #include <time.h> #include <time.h> #include <errno.h> #ifdef HAVE_SYS_TIME_H #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #include <sys/time.h> Loading Loading @@ -92,8 +94,8 @@ static const int days_per_month[] = /** Compute a time_t given a struct tm. The result is given in UTC, and /** Compute a time_t given a struct tm. The result is given in UTC, and * does not account for leap seconds. Return 0 on success, -1 on failure. * does not account for leap seconds. Return 0 on success, -1 on failure. */ */ int ATTR_UNUSED STATIC int tor_timegm(const struct tm *tm, time_t *time_out) tor_timegm_impl(const struct tm *tm, time_t *time_out) { { /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. * It's way more brute-force than fiddling with tzset(). * It's way more brute-force than fiddling with tzset(). Loading Loading @@ -162,6 +164,35 @@ tor_timegm(const struct tm *tm, time_t *time_out) return 0; return 0; } } /** Compute a time_t given a struct tm. The result here should be an inverse * of the system's gmtime() function. Return 0 on success, -1 on failure. */ int tor_timegm(const struct tm *tm, time_t *time_out) { #ifdef HAVE_TIMEGM /* If the system gives us a timegm(), use it: if the system's time_t * includes leap seconds, then we can hope that its timegm() knows too. * * https://k5wiki.kerberos.org/wiki/Leap_second_handling says the in * general we can rely on any system with leap seconds also having a * timegm implementation. Let's hope it's right! * */ time_t result = timegm((struct tm *) tm); if (result == -1) { log_warn(LD_BUG, "timegm() could not convert time: %s", strerror(errno)); *time_out = 0; return -1; } else { *time_out = result; return 0; } #else /* The system doesn't have timegm; we'll have to use our own. */ return tor_timegm_impl(tm, time_out); #endif } /* strftime is locale-specific, so we need to replace those parts */ /* strftime is locale-specific, so we need to replace those parts */ /** A c-locale array of 3-letter names of weekdays, starting with Sun. */ /** A c-locale array of 3-letter names of weekdays, starting with Sun. */ Loading src/lib/encoding/time_fmt.h +6 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <sys/types.h> #include <sys/types.h> #endif #endif #include "lib/testsupport/testsupport.h" struct tm; struct tm; struct timeval; struct timeval; Loading @@ -41,4 +43,8 @@ int parse_iso_time_nospace(const char *cp, time_t *t); int parse_http_time(const char *buf, struct tm *tm); int parse_http_time(const char *buf, struct tm *tm); int format_time_interval(char *out, size_t out_len, long interval); int format_time_interval(char *out, size_t out_len, long interval); #ifdef TIME_FMT_PRIVATE STATIC int tor_timegm_impl(const struct tm *tm, time_t *time_out); #endif #endif /* !defined(TOR_TIME_FMT_H) */ #endif /* !defined(TOR_TIME_FMT_H) */ src/test/test_util.c +25 −1 Original line number Original line Diff line number Diff line Loading @@ -7,6 +7,7 @@ #define COMPAT_TIME_PRIVATE #define COMPAT_TIME_PRIVATE #define UTIL_MALLOC_PRIVATE #define UTIL_MALLOC_PRIVATE #define PROCESS_WIN32_PRIVATE #define PROCESS_WIN32_PRIVATE #define TIME_FMT_PRIVATE #include "lib/testsupport/testsupport.h" #include "lib/testsupport/testsupport.h" #include "core/or/or.h" #include "core/or/or.h" #include "lib/buf/buffers.h" #include "lib/buf/buffers.h" Loading Loading @@ -111,7 +112,7 @@ static time_t tor_timegm_wrapper(const struct tm *tm) tor_timegm_wrapper(const struct tm *tm) { { time_t t; time_t t; if (tor_timegm(tm, &t) < 0) if (tor_timegm_impl(tm, &t) < 0) return -1; return -1; return t; return t; } } Loading Loading @@ -1501,6 +1502,28 @@ test_util_parse_http_time(void *arg) teardown_capture_of_logs(); teardown_capture_of_logs(); } } static void test_util_timegm_real(void *arg) { (void)arg; /* Get the real timegm again! We're not testing our impl; we want the * one that will actually get called. */ #undef tor_timegm /* Now check: is timegm the real inverse of gmtime? */ time_t now = time(NULL), time2=0; struct tm tm, *p; p = tor_gmtime_r(&now, &tm); tt_ptr_op(p, OP_NE, NULL); int r = tor_timegm(&tm, &time2); tt_int_op(r, OP_EQ, 0); tt_i64_op((int64_t) now, OP_EQ, (int64_t) time2); done: ; } static void static void test_util_config_line(void *arg) test_util_config_line(void *arg) { { Loading Loading @@ -7036,6 +7059,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(monotonic_time_ratchet, TT_FORK), UTIL_TEST(monotonic_time_ratchet, TT_FORK), UTIL_TEST(monotonic_time_zero, 0), UTIL_TEST(monotonic_time_zero, 0), UTIL_TEST(monotonic_time_add_msec, 0), UTIL_TEST(monotonic_time_add_msec, 0), UTIL_TEST(timegm_real, 0), UTIL_TEST(htonll, 0), UTIL_TEST(htonll, 0), UTIL_TEST(get_unquoted_path, 0), UTIL_TEST(get_unquoted_path, 0), UTIL_TEST(map_anon, 0), UTIL_TEST(map_anon, 0), Loading Loading
changes/bug40383 0 → 100644 +7 −0 Original line number Original line Diff line number Diff line o Minor bugfixes (timekeeping): - Calculate the time of day correctly on systems where the time_t type includes leap seconds. (This is not the case on most operating systems, but on those where it occurs, our tor_timegm function did not correctly invert the system's gmtime function, which could result in assertion failures when calculating voting schedules.) Fixes bug 40383; bugfix on 0.2.0.3-alpha.
configure.ac +1 −0 Original line number Original line Diff line number Diff line Loading @@ -806,6 +806,7 @@ AC_CHECK_FUNCS( strtoull \ strtoull \ sysconf \ sysconf \ sysctl \ sysctl \ timegm \ truncate \ truncate \ uname \ uname \ usleep \ usleep \ Loading
src/lib/encoding/time_fmt.c +33 −2 Original line number Original line Diff line number Diff line Loading @@ -13,6 +13,7 @@ * and handles a larger variety of types. It converts between different time * and handles a larger variety of types. It converts between different time * formats, and encodes and decodes them from strings. * formats, and encodes and decodes them from strings. **/ **/ #define TIME_FMT_PRIVATE #include "lib/encoding/time_fmt.h" #include "lib/encoding/time_fmt.h" #include "lib/log/log.h" #include "lib/log/log.h" Loading @@ -25,6 +26,7 @@ #include <string.h> #include <string.h> #include <time.h> #include <time.h> #include <errno.h> #ifdef HAVE_SYS_TIME_H #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #include <sys/time.h> Loading Loading @@ -92,8 +94,8 @@ static const int days_per_month[] = /** Compute a time_t given a struct tm. The result is given in UTC, and /** Compute a time_t given a struct tm. The result is given in UTC, and * does not account for leap seconds. Return 0 on success, -1 on failure. * does not account for leap seconds. Return 0 on success, -1 on failure. */ */ int ATTR_UNUSED STATIC int tor_timegm(const struct tm *tm, time_t *time_out) tor_timegm_impl(const struct tm *tm, time_t *time_out) { { /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. /* This is a pretty ironclad timegm implementation, snarfed from Python2.2. * It's way more brute-force than fiddling with tzset(). * It's way more brute-force than fiddling with tzset(). Loading Loading @@ -162,6 +164,35 @@ tor_timegm(const struct tm *tm, time_t *time_out) return 0; return 0; } } /** Compute a time_t given a struct tm. The result here should be an inverse * of the system's gmtime() function. Return 0 on success, -1 on failure. */ int tor_timegm(const struct tm *tm, time_t *time_out) { #ifdef HAVE_TIMEGM /* If the system gives us a timegm(), use it: if the system's time_t * includes leap seconds, then we can hope that its timegm() knows too. * * https://k5wiki.kerberos.org/wiki/Leap_second_handling says the in * general we can rely on any system with leap seconds also having a * timegm implementation. Let's hope it's right! * */ time_t result = timegm((struct tm *) tm); if (result == -1) { log_warn(LD_BUG, "timegm() could not convert time: %s", strerror(errno)); *time_out = 0; return -1; } else { *time_out = result; return 0; } #else /* The system doesn't have timegm; we'll have to use our own. */ return tor_timegm_impl(tm, time_out); #endif } /* strftime is locale-specific, so we need to replace those parts */ /* strftime is locale-specific, so we need to replace those parts */ /** A c-locale array of 3-letter names of weekdays, starting with Sun. */ /** A c-locale array of 3-letter names of weekdays, starting with Sun. */ Loading
src/lib/encoding/time_fmt.h +6 −0 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,8 @@ #include <sys/types.h> #include <sys/types.h> #endif #endif #include "lib/testsupport/testsupport.h" struct tm; struct tm; struct timeval; struct timeval; Loading @@ -41,4 +43,8 @@ int parse_iso_time_nospace(const char *cp, time_t *t); int parse_http_time(const char *buf, struct tm *tm); int parse_http_time(const char *buf, struct tm *tm); int format_time_interval(char *out, size_t out_len, long interval); int format_time_interval(char *out, size_t out_len, long interval); #ifdef TIME_FMT_PRIVATE STATIC int tor_timegm_impl(const struct tm *tm, time_t *time_out); #endif #endif /* !defined(TOR_TIME_FMT_H) */ #endif /* !defined(TOR_TIME_FMT_H) */
src/test/test_util.c +25 −1 Original line number Original line Diff line number Diff line Loading @@ -7,6 +7,7 @@ #define COMPAT_TIME_PRIVATE #define COMPAT_TIME_PRIVATE #define UTIL_MALLOC_PRIVATE #define UTIL_MALLOC_PRIVATE #define PROCESS_WIN32_PRIVATE #define PROCESS_WIN32_PRIVATE #define TIME_FMT_PRIVATE #include "lib/testsupport/testsupport.h" #include "lib/testsupport/testsupport.h" #include "core/or/or.h" #include "core/or/or.h" #include "lib/buf/buffers.h" #include "lib/buf/buffers.h" Loading Loading @@ -111,7 +112,7 @@ static time_t tor_timegm_wrapper(const struct tm *tm) tor_timegm_wrapper(const struct tm *tm) { { time_t t; time_t t; if (tor_timegm(tm, &t) < 0) if (tor_timegm_impl(tm, &t) < 0) return -1; return -1; return t; return t; } } Loading Loading @@ -1501,6 +1502,28 @@ test_util_parse_http_time(void *arg) teardown_capture_of_logs(); teardown_capture_of_logs(); } } static void test_util_timegm_real(void *arg) { (void)arg; /* Get the real timegm again! We're not testing our impl; we want the * one that will actually get called. */ #undef tor_timegm /* Now check: is timegm the real inverse of gmtime? */ time_t now = time(NULL), time2=0; struct tm tm, *p; p = tor_gmtime_r(&now, &tm); tt_ptr_op(p, OP_NE, NULL); int r = tor_timegm(&tm, &time2); tt_int_op(r, OP_EQ, 0); tt_i64_op((int64_t) now, OP_EQ, (int64_t) time2); done: ; } static void static void test_util_config_line(void *arg) test_util_config_line(void *arg) { { Loading Loading @@ -7036,6 +7059,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(monotonic_time_ratchet, TT_FORK), UTIL_TEST(monotonic_time_ratchet, TT_FORK), UTIL_TEST(monotonic_time_zero, 0), UTIL_TEST(monotonic_time_zero, 0), UTIL_TEST(monotonic_time_add_msec, 0), UTIL_TEST(monotonic_time_add_msec, 0), UTIL_TEST(timegm_real, 0), UTIL_TEST(htonll, 0), UTIL_TEST(htonll, 0), UTIL_TEST(get_unquoted_path, 0), UTIL_TEST(get_unquoted_path, 0), UTIL_TEST(map_anon, 0), UTIL_TEST(map_anon, 0), Loading