Skip to content

Commit

Permalink
Merge bitcoin#31549: fuzz: Abort if system time is called without moc…
Browse files Browse the repository at this point in the history
…k time being set

a96b84c fuzz: Abort when calling system time without setting mock time (marcofleon)
ff21870 fuzz: Add SetMockTime() to necessary targets (marcofleon)

Pull request description:

  This PR expands the `CheckGlobals` utility that was introduced in bitcoin#31486 and should help with fuzz stability (bitcoin#29018).

  System time shouldn't be used when running a fuzz test, as it is likely to introduce instability (non-determinism). This PR identifies and fixes the targets that were calling system time without setting mock time at the start of an iteration.

  Removing`SetMockTime()` from any one of these targets should result in a crash and a message describing the issue.

ACKs for top commit:
  achow101:
    ACK a96b84c
  dergoegge:
    Code review ACK a96b84c
  brunoerg:
    crACK a96b84c

Tree-SHA512: e093a9feb8a397954f7b1416dfa8790b2733f09d5ac51fda5a9d225a55ebd8f99135aa52bdf5ab531653ad1a3739c4ca2b5349c1d989bb4b009ec8eaad684f7d
  • Loading branch information
achow101 committed Jan 10, 2025
2 parents 54115d8 + a96b84c commit 37af8bf
Show file tree
Hide file tree
Showing 17 changed files with 65 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/test/fuzz/fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Sock> { std::terminate(); };
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/load_external_block_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <util/time.h>
#include <validation.h>

#include <cstdint>
Expand All @@ -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()) {
Expand Down
7 changes: 7 additions & 0 deletions src/test/fuzz/mini_miner.cpp
Original file line number Diff line number Diff line change
@@ -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/fuzz.h>
#include <test/fuzz/util.h>
Expand All @@ -13,6 +17,7 @@
#include <random.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/time.h>
#include <util/translation.h>

#include <deque>
Expand All @@ -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());
Expand Down Expand Up @@ -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());
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <test/util/setup_common.h>
#include <util/asmap.h>
#include <util/chaintype.h>
#include <util/time.h>

#include <cstdint>
#include <optional>
Expand Down Expand Up @@ -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)};
{
Expand Down
5 changes: 3 additions & 2 deletions src/test/fuzz/p2p_headers_presync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/partially_downloaded_block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/time.h>
#include <util/translation.h>

#include <cstddef>
Expand Down Expand Up @@ -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<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS)};
if (!block || block->vtx.size() == 0 ||
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/socks5.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <test/fuzz/util.h>
#include <test/fuzz/util/net.h>
#include <test/util/setup_common.h>
#include <util/time.h>

#include <cstdint>
#include <string>
Expand All @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/test/fuzz/txdownloadman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <test/util/txmempool.h>
#include <util/hasher.h>
#include <util/rbf.h>
#include <util/time.h>
#include <txmempool.h>
#include <validation.h>
#include <validationinterface.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions src/test/fuzz/util/check_globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <test/fuzz/util/check_globals.h>

#include <test/util/random.h>
#include <util/time.h>

#include <iostream>
#include <memory>
Expand All @@ -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()
{
Expand All @@ -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();
}
}
};

Expand Down
3 changes: 3 additions & 0 deletions src/test/fuzz/util/check_globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
#ifndef BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H
#define BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H

#include <atomic>
#include <memory>
#include <optional>
#include <string>

extern std::atomic<bool> g_used_system_time;

struct CheckGlobalsImpl;
struct CheckGlobals {
CheckGlobals();
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/utxo_snapshot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <util/check.h>
#include <util/fs.h>
#include <util/result.h>
#include <util/time.h>
#include <validation.h>

#include <cstdint>
Expand Down Expand Up @@ -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};
Expand Down
9 changes: 7 additions & 2 deletions src/test/fuzz/utxo_total_supply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,30 @@
#include <test/util/mining.h>
#include <test/util/setup_common.h>
#include <util/chaintype.h>
#include <util/time.h>
#include <validation.h>

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());
Expand Down
4 changes: 3 additions & 1 deletion src/test/util/setup_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions src/util/time.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@
void UninterruptibleSleep(const std::chrono::microseconds& n) { std::this_thread::sleep_for(n); }

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
{
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 :
Expand Down
2 changes: 2 additions & 0 deletions src/wallet/test/fuzz/notifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <uint256.h>
#include <util/check.h>
#include <util/result.h>
#include <util/time.h>
#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/context.h>
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/wallet/test/fuzz/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <test/fuzz/util/descriptor.h>
#include <test/util/setup_common.h>
#include <util/check.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <wallet/scriptpubkeyman.h>
Expand Down Expand Up @@ -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<CWallet> wallet_ptr{std::make_unique<CWallet>(node.chain.get(), "", CreateMockableWalletDatabase())};
Expand Down
2 changes: 2 additions & 0 deletions src/wallet/test/fuzz/spend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <test/fuzz/util/wallet.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/time.h>
#include <wallet/coincontrol.h>
#include <wallet/context.h>
#include <wallet/spend.h>
Expand All @@ -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;
Expand Down

0 comments on commit 37af8bf

Please sign in to comment.