mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-20 05:42:50 +02:00
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 <aj@erisian.com.au>
This commit is contained in:
parent
36be9b821a
commit
3c2e16be22
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user