diff --git a/src/init.cpp b/src/init.cpp index e6e4944ee..dbe592b28 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -833,6 +833,10 @@ bool InitSanityCheck(void) if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; + if (!ChronoSanityCheck()) { + return InitError("Clock epoch mismatch. Aborting."); + } + return true; } diff --git a/src/test/sanity_tests.cpp b/src/test/sanity_tests.cpp index cfb56b82a..c69a8c470 100644 --- a/src/test/sanity_tests.cpp +++ b/src/test/sanity_tests.cpp @@ -6,6 +6,7 @@ #include "compat/sanity.h" #include "key.h" #include "test/test_bitcoin.h" +#include "util/time.h" #include @@ -16,6 +17,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(CKey::ECC_InitSanityCheck() == true, "ECC 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 68794829d..dcc0ac3be 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -25,6 +25,50 @@ int64_t GetTime() return time(NULL); } +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; +} + + void SetMockTime(int64_t nMockTimeIn) { nMockTime = nMockTimeIn; diff --git a/src/util/time.h b/src/util/time.h index a563da702..04d6db82b 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -18,4 +18,7 @@ void MilliSleep(int64_t n); std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); +/** Sanity check epoch match normal Unix epoch */ +bool ChronoSanityCheck(); + #endif // BITCOIN_UTIL_TIME_H