This commit is contained in:
marcofleon 2025-01-08 18:14:29 +00:00 committed by GitHub
commit 58800b2645
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 65 additions and 5 deletions

View file

@ -116,6 +116,9 @@ void initialize()
// - Creating a BasicTestingSetup or derived class will switch to a random seed. // - Creating a BasicTestingSetup or derived class will switch to a random seed.
SeedRandomStateForTest(SeedRand::ZEROS); 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. // Terminate immediately if a fuzzing harness ever tries to create a socket.
// Individual tests can override this by pointing CreateSock to a mocked alternative. // Individual tests can override this by pointing CreateSock to a mocked alternative.
CreateSock = [](int, int, int) -> std::unique_ptr<Sock> { std::terminate(); }; CreateSock = [](int, int, int) -> std::unique_ptr<Sock> { std::terminate(); };

View file

@ -9,6 +9,7 @@
#include <test/fuzz/fuzz.h> #include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h> #include <test/fuzz/util.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/time.h>
#include <validation.h> #include <validation.h>
#include <cstdint> #include <cstdint>
@ -27,6 +28,7 @@ void initialize_load_external_block_file()
FUZZ_TARGET(load_external_block_file, .init = 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()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
FuzzedFileProvider fuzzed_file_provider{fuzzed_data_provider}; FuzzedFileProvider fuzzed_file_provider{fuzzed_data_provider};
AutoFile fuzzed_block_file{fuzzed_file_provider.open()}; AutoFile fuzzed_block_file{fuzzed_file_provider.open()};
if (fuzzed_block_file.IsNull()) { if (fuzzed_block_file.IsNull()) {

View file

@ -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 <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h> #include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h> #include <test/fuzz/util.h>
@ -13,6 +17,7 @@
#include <random.h> #include <random.h>
#include <txmempool.h> #include <txmempool.h>
#include <util/check.h> #include <util/check.h>
#include <util/time.h>
#include <util/translation.h> #include <util/translation.h>
#include <deque> #include <deque>
@ -36,6 +41,7 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
bilingual_str error; bilingual_str error;
CTxMemPool pool{CTxMemPool::Options{}, error}; CTxMemPool pool{CTxMemPool::Options{}, error};
Assert(error.empty()); Assert(error.empty());
@ -115,6 +121,7 @@ FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
bilingual_str error; bilingual_str error;
CTxMemPool pool{CTxMemPool::Options{}, error}; CTxMemPool pool{CTxMemPool::Options{}, error};
Assert(error.empty()); Assert(error.empty());

View file

@ -16,6 +16,7 @@
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/asmap.h> #include <util/asmap.h>
#include <util/chaintype.h> #include <util/chaintype.h>
#include <util/time.h>
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
@ -79,6 +80,7 @@ FUZZ_TARGET(net, .init = initialize_net)
FUZZ_TARGET(local_address, .init = initialize_net) FUZZ_TARGET(local_address, .init = initialize_net)
{ {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
CService service{ConsumeService(fuzzed_data_provider)}; CService service{ConsumeService(fuzzed_data_provider)};
CNode node{ConsumeNode(fuzzed_data_provider)}; CNode node{ConsumeNode(fuzzed_data_provider)};
{ {

View file

@ -154,14 +154,15 @@ void initialize()
FUZZ_TARGET(p2p_headers_presync, .init = initialize) FUZZ_TARGET(p2p_headers_presync, .init = initialize)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
ChainstateManager& chainman = *g_testing_setup->m_node.chainman; ChainstateManager& chainman = *g_testing_setup->m_node.chainman;
LOCK(NetEventsInterface::g_msgproc_mutex); LOCK(NetEventsInterface::g_msgproc_mutex);
g_testing_setup->ResetAndInitialize(); g_testing_setup->ResetAndInitialize();
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
CBlockHeader base{chainman.GetParams().GenesisBlock()}; CBlockHeader base{chainman.GetParams().GenesisBlock()};
SetMockTime(base.nTime); SetMockTime(base.nTime);

View file

@ -11,6 +11,7 @@
#include <test/util/txmempool.h> #include <test/util/txmempool.h>
#include <txmempool.h> #include <txmempool.h>
#include <util/check.h> #include <util/check.h>
#include <util/time.h>
#include <util/translation.h> #include <util/translation.h>
#include <cstddef> #include <cstddef>
@ -46,6 +47,7 @@ FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
auto block{ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS)}; auto block{ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS)};
if (!block || block->vtx.size() == 0 || if (!block || block->vtx.size() == 0 ||

View file

@ -9,6 +9,7 @@
#include <test/fuzz/util.h> #include <test/fuzz/util.h>
#include <test/fuzz/util/net.h> #include <test/fuzz/util/net.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/time.h>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
@ -29,6 +30,7 @@ void initialize_socks5()
FUZZ_TARGET(socks5, .init = initialize_socks5) FUZZ_TARGET(socks5, .init = initialize_socks5)
{ {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
ProxyCredentials proxy_credentials; ProxyCredentials proxy_credentials;
proxy_credentials.username = fuzzed_data_provider.ConsumeRandomLengthString(512); proxy_credentials.username = fuzzed_data_provider.ConsumeRandomLengthString(512);
proxy_credentials.password = fuzzed_data_provider.ConsumeRandomLengthString(512); proxy_credentials.password = fuzzed_data_provider.ConsumeRandomLengthString(512);

View file

@ -18,6 +18,7 @@
#include <test/util/txmempool.h> #include <test/util/txmempool.h>
#include <util/hasher.h> #include <util/hasher.h>
#include <util/rbf.h> #include <util/rbf.h>
#include <util/time.h>
#include <txmempool.h> #include <txmempool.h>
#include <validation.h> #include <validation.h>
#include <validationinterface.h> #include <validationinterface.h>
@ -167,6 +168,7 @@ FUZZ_TARGET(txdownloadman, .init = initialize)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
// Initialize txdownloadman // Initialize txdownloadman
bilingual_str error; bilingual_str error;
@ -297,6 +299,7 @@ FUZZ_TARGET(txdownloadman_impl, .init = initialize)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
// Initialize a TxDownloadManagerImpl // Initialize a TxDownloadManagerImpl
bilingual_str error; bilingual_str error;

View file

@ -5,6 +5,7 @@
#include <test/fuzz/util/check_globals.h> #include <test/fuzz/util/check_globals.h>
#include <test/util/random.h> #include <test/util/random.h>
#include <util/time.h>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@ -16,6 +17,8 @@ struct CheckGlobalsImpl {
{ {
g_used_g_prng = false; g_used_g_prng = false;
g_seeded_g_prng_zero = false; g_seeded_g_prng_zero = false;
g_used_system_time = false;
SetMockTime(0s);
} }
~CheckGlobalsImpl() ~CheckGlobalsImpl()
{ {
@ -34,6 +37,19 @@ struct CheckGlobalsImpl {
<< std::endl; << std::endl;
std::abort(); // Abort, because AFL may try to recover from a std::exit 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();
}
} }
}; };

View file

@ -5,10 +5,13 @@
#ifndef BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H #ifndef BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H
#define BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H #define BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H
#include <atomic>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string> #include <string>
extern std::atomic<bool> g_used_system_time;
struct CheckGlobalsImpl; struct CheckGlobalsImpl;
struct CheckGlobals { struct CheckGlobals {
CheckGlobals(); CheckGlobals();

View file

@ -24,6 +24,7 @@
#include <util/check.h> #include <util/check.h>
#include <util/fs.h> #include <util/fs.h>
#include <util/result.h> #include <util/result.h>
#include <util/time.h>
#include <validation.h> #include <validation.h>
#include <cstdint> #include <cstdint>
@ -72,6 +73,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider, /*min=*/1296688602)); // regtest genesis block timestamp
auto& setup{*g_setup}; auto& setup{*g_setup};
bool dirty_chainman{false}; // Re-use the global chainman, but reset it when it is dirty bool dirty_chainman{false}; // Re-use the global chainman, but reset it when it is dirty
auto& chainman{*setup.m_node.chainman}; auto& chainman{*setup.m_node.chainman};

View file

@ -15,6 +15,7 @@
#include <test/util/mining.h> #include <test/util/mining.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/chaintype.h> #include <util/chaintype.h>
#include <util/time.h>
#include <validation.h> #include <validation.h>
using node::BlockAssembler; using node::BlockAssembler;
@ -22,18 +23,22 @@ using node::BlockAssembler;
FUZZ_TARGET(utxo_total_supply) FUZZ_TARGET(utxo_total_supply)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); 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) */ /** The testing setup that creates a chainman only (no chainstate) */
ChainTestingSetup test_setup{ ChainTestingSetup test_setup{
ChainType::REGTEST, ChainType::REGTEST,
{ {
.extra_args = {"-testactivationheight=bip34@2"}, .extra_args = {
"-testactivationheight=bip34@2",
strprintf("-mocktime=%d", mock_time).c_str()
},
}, },
}; };
// Create chainstate // Create chainstate
test_setup.LoadVerifyActivateChainstate(); test_setup.LoadVerifyActivateChainstate();
auto& node{test_setup.m_node}; auto& node{test_setup.m_node};
auto& chainman{*Assert(test_setup.m_node.chainman)}; auto& chainman{*Assert(test_setup.m_node.chainman)};
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto ActiveHeight = [&]() { const auto ActiveHeight = [&]() {
LOCK(chainman.GetMutex()); LOCK(chainman.GetMutex());

View file

@ -199,7 +199,9 @@ BasicTestingSetup::~BasicTestingSetup()
{ {
m_node.ecc_context.reset(); m_node.ecc_context.reset();
m_node.kernel.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(); LogInstance().DisconnectTestLogger();
if (m_has_custom_datadir) { if (m_has_custom_datadir) {
// Only remove the lock file, preserve the data directory. // Only remove the lock file, preserve the data directory.

View file

@ -20,10 +20,14 @@
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); } void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }
static std::atomic<std::chrono::seconds> g_mock_time{}; //!< For testing static std::atomic<std::chrono::seconds> g_mock_time{}; //!< For testing
std::atomic<bool> g_used_system_time{false};
NodeClock::time_point NodeClock::now() noexcept NodeClock::time_point NodeClock::now() noexcept
{ {
const auto mocktime{g_mock_time.load(std::memory_order_relaxed)}; const auto mocktime{g_mock_time.load(std::memory_order_relaxed)};
if (!mocktime.count()) {
g_used_system_time = true;
}
const auto ret{ const auto ret{
mocktime.count() ? mocktime.count() ?
mocktime : mocktime :

View file

@ -24,6 +24,7 @@
#include <uint256.h> #include <uint256.h>
#include <util/check.h> #include <util/check.h>
#include <util/result.h> #include <util/result.h>
#include <util/time.h>
#include <util/translation.h> #include <util/translation.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/context.h> #include <wallet/context.h>
@ -58,6 +59,7 @@ FUZZ_TARGET(wallet_notifications, .init = initialize_setup)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; 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 // 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 // without fee. Thus, the balance of the wallets should always equal the
// total amount. // total amount.

View file

@ -19,6 +19,7 @@
#include <test/fuzz/util/descriptor.h> #include <test/fuzz/util/descriptor.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/check.h> #include <util/check.h>
#include <util/time.h>
#include <util/translation.h> #include <util/translation.h>
#include <validation.h> #include <validation.h>
#include <wallet/scriptpubkeyman.h> #include <wallet/scriptpubkeyman.h>
@ -87,6 +88,7 @@ FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
const auto& node{g_setup->m_node}; const auto& node{g_setup->m_node};
Chainstate& chainstate{node.chainman->ActiveChainstate()}; Chainstate& chainstate{node.chainman->ActiveChainstate()};
std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())}; std::unique_ptr<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())};

View file

@ -8,6 +8,7 @@
#include <test/fuzz/util/wallet.h> #include <test/fuzz/util/wallet.h>
#include <test/util/random.h> #include <test/util/random.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/time.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
#include <wallet/context.h> #include <wallet/context.h>
#include <wallet/spend.h> #include <wallet/spend.h>
@ -32,6 +33,7 @@ FUZZ_TARGET(wallet_create_transaction, .init = initialize_setup)
{ {
SeedRandomStateForTest(SeedRand::ZEROS); SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
const auto& node = g_setup->m_node; const auto& node = g_setup->m_node;
Chainstate& chainstate{node.chainman->ActiveChainstate()}; Chainstate& chainstate{node.chainman->ActiveChainstate()};
ArgsManager& args = *node.args; ArgsManager& args = *node.args;