From 3c2e16be22ae04bf56663ee5ec1554d0d569741b Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Mon, 16 Jan 2017 15:01:37 -0500 Subject: [PATCH 1/2] time: add runtime sanity check std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed to use the Unix epoch timestamp, but in practice they almost certainly will. Any differing behavior will be assumed to be an error, unless certain platforms prove to consistently deviate, at which point we'll cope with it by adding offsets. Do a quick runtime check to verify that time_t(0) == std::chrono::system_clock's epoch time == unix epoch. Co-authored-by: Anthony Towns --- src/init.cpp | 4 ++++ src/test/sanity_tests.cpp | 2 ++ src/util/time.cpp | 43 +++++++++++++++++++++++++++++++++++++++ src/util/time.h | 3 +++ 4 files changed, 52 insertions(+) diff --git a/src/init.cpp b/src/init.cpp index 96fb32ce2ad..3beb421bf96 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -773,6 +773,10 @@ static bool InitSanityCheck() return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting.")); } + if (!ChronoSanityCheck()) { + return InitError(Untranslated("Clock epoch mismatch. Aborting.")); + } + return true; } diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp index 740b2c72dbc..3e4b963fe36 100644 --- a/src/test/sanity_tests.cpp +++ b/src/test/sanity_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -15,6 +16,7 @@ BOOST_AUTO_TEST_CASE(basic_sanity) BOOST_CHECK_MESSAGE(glibc_sanity_test() == true, "libc sanity test"); BOOST_CHECK_MESSAGE(glibcxx_sanity_test() == true, "stdlib sanity test"); BOOST_CHECK_MESSAGE(ECC_InitSanityCheck() == true, "secp256k1 sanity test"); + BOOST_CHECK_MESSAGE(ChronoSanityCheck() == true, "chrono epoch test"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/util/time.cpp b/src/util/time.cpp index 295806c54af..9631c115e9c 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -33,6 +33,49 @@ int64_t GetTime() return now; } +bool ChronoSanityCheck() +{ + // std::chrono::system_clock.time_since_epoch and time_t(0) are not guaranteed + // to use the Unix epoch timestamp, prior to C++20, but in practice they almost + // certainly will. Any differing behavior will be assumed to be an error, unless + // certain platforms prove to consistently deviate, at which point we'll cope + // with it by adding offsets. + + // Create a new clock from time_t(0) and make sure that it represents 0 + // seconds from the system_clock's time_since_epoch. Then convert that back + // to a time_t and verify that it's the same as before. + const time_t time_t_epoch{}; + auto clock = std::chrono::system_clock::from_time_t(time_t_epoch); + if (std::chrono::duration_cast(clock.time_since_epoch()).count() != 0) { + return false; + } + + time_t time_val = std::chrono::system_clock::to_time_t(clock); + if (time_val != time_t_epoch) { + return false; + } + + // Check that the above zero time is actually equal to the known unix timestamp. + struct tm epoch; +#ifdef HAVE_GMTIME_R + if (gmtime_r(&time_val, &epoch) == nullptr) { +#else + if (gmtime_s(&epoch, &time_val) != 0) { +#endif + return false; + } + + if ((epoch.tm_sec != 0) || + (epoch.tm_min != 0) || + (epoch.tm_hour != 0) || + (epoch.tm_mday != 1) || + (epoch.tm_mon != 0) || + (epoch.tm_year != 70)) { + return false; + } + return true; +} + template T GetTime() { diff --git a/src/util/time.h b/src/util/time.h index 03b75b5be56..6fb6d5a670f 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -70,4 +70,7 @@ struct timeval MillisToTimeval(int64_t nTimeout); */ struct timeval MillisToTimeval(std::chrono::milliseconds ms); +/** Sanity check epoch match normal Unix epoch */ +bool ChronoSanityCheck(); + #endif // BITCOIN_UTIL_TIME_H From 9266f7497f256d780178829e0f3a29ddaeb794ba Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 21 Feb 2020 13:12:40 -0500 Subject: [PATCH 2/2] util: Use std::chrono for time getters --- src/util/time.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/util/time.cpp b/src/util/time.cpp index 9631c115e9c..06ed8f45169 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -90,6 +90,14 @@ template std::chrono::seconds GetTime(); template std::chrono::milliseconds GetTime(); template std::chrono::microseconds GetTime(); +template +static T GetSystemTime() +{ + const auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + assert(now.count() > 0); + return now; +} + void SetMockTime(int64_t nMockTimeIn) { Assert(nMockTimeIn >= 0); @@ -103,23 +111,17 @@ int64_t GetMockTime() int64_t GetTimeMillis() { - int64_t now = (boost::posix_time::microsec_clock::universal_time() - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_milliseconds(); - assert(now > 0); - return now; + return int64_t{GetSystemTime().count()}; } int64_t GetTimeMicros() { - int64_t now = (boost::posix_time::microsec_clock::universal_time() - - boost::posix_time::ptime(boost::gregorian::date(1970,1,1))).total_microseconds(); - assert(now > 0); - return now; + return int64_t{GetSystemTime().count()}; } int64_t GetSystemTimeInSeconds() { - return GetTimeMicros()/1000000; + return int64_t{GetSystemTime().count()}; } std::string FormatISO8601DateTime(int64_t nTime) {