aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/init.cpp4
-rw-r--r--src/test/sanity_tests.cpp2
-rw-r--r--src/util/time.cpp43
-rw-r--r--src/util/time.h3
4 files changed, 52 insertions, 0 deletions
diff --git a/src/init.cpp b/src/init.cpp
index 96fb32ce2a..3beb421bf9 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 740b2c72db..3e4b963fe3 100644
--- a/src/test/sanity_tests.cpp
+++ b/src/test/sanity_tests.cpp
@@ -5,6 +5,7 @@
#include <compat/sanity.h>
#include <key.h>
#include <test/util/setup_common.h>
+#include <util/time.h>
#include <boost/test/unit_test.hpp>
@@ -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 295806c54a..9631c115e9 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<std::chrono::seconds>(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 <typename T>
T GetTime()
{
diff --git a/src/util/time.h b/src/util/time.h
index 03b75b5be5..6fb6d5a670 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