diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index e4e4723c74086..5b1d0cda7aaa5 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -116,6 +116,9 @@ void initialize() // - Creating a BasicTestingSetup or derived class will switch to a random seed. SeedRandomStateForTest(SeedRand::ZEROS); + // Set time to the genesis block timestamp for deterministic initialization. + SetMockTime(1231006505); + // Terminate immediately if a fuzzing harness ever tries to create a socket. // Individual tests can override this by pointing CreateSock to a mocked alternative. CreateSock = [](int, int, int) -> std::unique_ptr { std::terminate(); }; diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index 6460261f0f5e3..b5293dd11a5f3 100644 --- a/src/test/fuzz/load_external_block_file.cpp +++ b/src/test/fuzz/load_external_block_file.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,7 @@ void initialize_load_external_block_file() FUZZ_TARGET(load_external_block_file, .init = initialize_load_external_block_file) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); FuzzedFileProvider fuzzed_file_provider{fuzzed_data_provider}; AutoFile fuzzed_block_file{fuzzed_file_provider.open()}; if (fuzzed_block_file.IsNull()) { diff --git a/src/test/fuzz/mini_miner.cpp b/src/test/fuzz/mini_miner.cpp index a5bccd103d267..817bc0f18e566 100644 --- a/src/test/fuzz/mini_miner.cpp +++ b/src/test/fuzz/mini_miner.cpp @@ -1,3 +1,7 @@ +// Copyright (c) The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include #include #include @@ -13,6 +17,7 @@ #include #include #include +#include #include #include @@ -36,6 +41,7 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); bilingual_str error; CTxMemPool pool{CTxMemPool::Options{}, error}; Assert(error.empty()); @@ -115,6 +121,7 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); bilingual_str error; CTxMemPool pool{CTxMemPool::Options{}, error}; Assert(error.empty()); diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index 4cec96274e7a2..1a0de7aa3631e 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -79,6 +80,7 @@ FUZZ_TARGET(net, .init = initialize_net) FUZZ_TARGET(local_address, .init = initialize_net) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + SetMockTime(ConsumeTime(fuzzed_data_provider)); CService service{ConsumeService(fuzzed_data_provider)}; CNode node{ConsumeNode(fuzzed_data_provider)}; { diff --git a/src/test/fuzz/p2p_headers_presync.cpp b/src/test/fuzz/p2p_headers_presync.cpp index 873eb2b1cc9c3..ed7041ad1f1ad 100644 --- a/src/test/fuzz/p2p_headers_presync.cpp +++ b/src/test/fuzz/p2p_headers_presync.cpp @@ -154,14 +154,15 @@ void initialize() FUZZ_TARGET(p2p_headers_presync, .init = initialize) { SeedRandomStateForTest(SeedRand::ZEROS); + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); + ChainstateManager& chainman = *g_testing_setup->m_node.chainman; LOCK(NetEventsInterface::g_msgproc_mutex); g_testing_setup->ResetAndInitialize(); - FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; - CBlockHeader base{chainman.GetParams().GenesisBlock()}; SetMockTime(base.nTime); diff --git a/src/test/fuzz/partially_downloaded_block.cpp b/src/test/fuzz/partially_downloaded_block.cpp index 8a42807be814e..82d781cd53c27 100644 --- a/src/test/fuzz/partially_downloaded_block.cpp +++ b/src/test/fuzz/partially_downloaded_block.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -46,6 +47,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); auto block{ConsumeDeserializable(fuzzed_data_provider, TX_WITH_WITNESS)}; if (!block || block->vtx.size() == 0 || diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp index 17d17875865ed..2e6e2a94acaf4 100644 --- a/src/test/fuzz/socks5.cpp +++ b/src/test/fuzz/socks5.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,7 @@ void initialize_socks5() FUZZ_TARGET(socks5, .init = initialize_socks5) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); ProxyCredentials proxy_credentials; proxy_credentials.username = fuzzed_data_provider.ConsumeRandomLengthString(512); proxy_credentials.password = fuzzed_data_provider.ConsumeRandomLengthString(512); diff --git a/src/test/fuzz/txdownloadman.cpp b/src/test/fuzz/txdownloadman.cpp index 4917e8b4053fa..bb1331c37c8c4 100644 --- a/src/test/fuzz/txdownloadman.cpp +++ b/src/test/fuzz/txdownloadman.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -167,6 +168,7 @@ FUZZ_TARGET(txdownloadman, .init = initialize) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + SetMockTime(ConsumeTime(fuzzed_data_provider)); // Initialize txdownloadman bilingual_str error; @@ -297,6 +299,7 @@ FUZZ_TARGET(txdownloadman_impl, .init = initialize) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + SetMockTime(ConsumeTime(fuzzed_data_provider)); // Initialize a TxDownloadManagerImpl bilingual_str error; diff --git a/src/test/fuzz/util/check_globals.cpp b/src/test/fuzz/util/check_globals.cpp index fbc5a55598bef..f91a965afced8 100644 --- a/src/test/fuzz/util/check_globals.cpp +++ b/src/test/fuzz/util/check_globals.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -16,6 +17,8 @@ struct CheckGlobalsImpl { { g_used_g_prng = false; g_seeded_g_prng_zero = false; + g_used_system_time = false; + SetMockTime(0s); } ~CheckGlobalsImpl() { @@ -34,6 +37,19 @@ struct CheckGlobalsImpl { << std::endl; std::abort(); // Abort, because AFL may try to recover from a std::exit } + + if (g_used_system_time) { + std::cerr << "\n\n" + "The current fuzz target accessed system time.\n\n" + + "This is acceptable, but requires the fuzz target to call \n" + "SetMockTime() at the beginning of processing the fuzz input.\n\n" + + "Without setting mock time, time-dependent behavior can lead \n" + "to non-reproducible bugs or inefficient fuzzing.\n\n" + << std::endl; + std::abort(); + } } }; diff --git a/src/test/fuzz/util/check_globals.h b/src/test/fuzz/util/check_globals.h index 79f247535ae85..12d39c2daf551 100644 --- a/src/test/fuzz/util/check_globals.h +++ b/src/test/fuzz/util/check_globals.h @@ -5,10 +5,13 @@ #ifndef BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H #define BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H +#include #include #include #include +extern std::atomic g_used_system_time; + struct CheckGlobalsImpl; struct CheckGlobals { CheckGlobals(); diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index 9fe922cfafe3c..5eb6a32320249 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -72,6 +73,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + SetMockTime(ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)); // regtest genesis block timestamp auto& setup{*g_setup}; bool dirty_chainman{false}; // Re-use the global chainman, but reset it when it is dirty auto& chainman{*setup.m_node.chainman}; diff --git a/src/test/fuzz/utxo_total_supply.cpp b/src/test/fuzz/utxo_total_supply.cpp index 84d82f71e2898..e574c5b85c5a3 100644 --- a/src/test/fuzz/utxo_total_supply.cpp +++ b/src/test/fuzz/utxo_total_supply.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include using node::BlockAssembler; @@ -22,18 +23,22 @@ using node::BlockAssembler; FUZZ_TARGET(utxo_total_supply) { SeedRandomStateForTest(SeedRand::ZEROS); + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const auto mock_time{ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)}; // regtest genesis block timestamp /** The testing setup that creates a chainman only (no chainstate) */ ChainTestingSetup test_setup{ ChainType::REGTEST, { - .extra_args = {"-testactivationheight=bip34@2"}, + .extra_args = { + "-testactivationheight=bip34@2", + strprintf("-mocktime=%d", mock_time).c_str() + }, }, }; // Create chainstate test_setup.LoadVerifyActivateChainstate(); auto& node{test_setup.m_node}; auto& chainman{*Assert(test_setup.m_node.chainman)}; - FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const auto ActiveHeight = [&]() { LOCK(chainman.GetMutex()); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 3f8c6f41ba1f5..1ebc3464d84e4 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -199,7 +199,9 @@ BasicTestingSetup::~BasicTestingSetup() { m_node.ecc_context.reset(); m_node.kernel.reset(); - SetMockTime(0s); // Reset mocktime for following tests + if constexpr (!G_FUZZING) { + SetMockTime(0s); // Reset mocktime for following tests + } LogInstance().DisconnectTestLogger(); if (m_has_custom_datadir) { // Only remove the lock file, preserve the data directory. diff --git a/src/util/time.cpp b/src/util/time.cpp index e20f30a474570..00f0f4739260d 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -20,10 +20,14 @@ void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); } static std::atomic g_mock_time{}; //!< For testing +std::atomic g_used_system_time{false}; NodeClock::time_point NodeClock::now() noexcept { const auto mocktime{g_mock_time.load(std::memory_order_relaxed)}; + if (!mocktime.count()) { + g_used_system_time = true; + } const auto ret{ mocktime.count() ? mocktime : diff --git a/src/wallet/test/fuzz/notifications.cpp b/src/wallet/test/fuzz/notifications.cpp index 7b85fb26fc97e..b1302e3b35d81 100644 --- a/src/wallet/test/fuzz/notifications.cpp +++ b/src/wallet/test/fuzz/notifications.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ FUZZ_TARGET(wallet_notifications, .init = initialize_setup) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); // The total amount, to be distributed to the wallets a and b in txs // without fee. Thus, the balance of the wallets should always equal the // total amount. diff --git a/src/wallet/test/fuzz/scriptpubkeyman.cpp b/src/wallet/test/fuzz/scriptpubkeyman.cpp index b0d8628a91cee..0c17a6bf7a9f6 100644 --- a/src/wallet/test/fuzz/scriptpubkeyman.cpp +++ b/src/wallet/test/fuzz/scriptpubkeyman.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +88,7 @@ FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); const auto& node{g_setup->m_node}; Chainstate& chainstate{node.chainman->ActiveChainstate()}; std::unique_ptr wallet_ptr{std::make_unique(node.chain.get(), "", CreateMockableWalletDatabase())}; diff --git a/src/wallet/test/fuzz/spend.cpp b/src/wallet/test/fuzz/spend.cpp index c4c04bce4b00b..552364a667d08 100644 --- a/src/wallet/test/fuzz/spend.cpp +++ b/src/wallet/test/fuzz/spend.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ FUZZ_TARGET(wallet_create_transaction, .init = initialize_setup) { SeedRandomStateForTest(SeedRand::ZEROS); FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + SetMockTime(ConsumeTime(fuzzed_data_provider)); const auto& node = g_setup->m_node; Chainstate& chainstate{node.chainman->ActiveChainstate()}; ArgsManager& args = *node.args;