Files
bitcoin/src/test/fuzz/p2p_handshake.cpp
Hao Xu b11456386b fuzz: let the test input toggle IBD in the p2p fuzz targets
process_message, process_messages and p2p_handshake put the node in
IBD via ResetIbd(), so net_processing returns early at the
IsInitialBlockDownload() check and almost never exercises the non-IBD
transaction-handling code paths behind it.

Only ConnectTip() latches the node back out, through
UpdateIBDStatus(). p2p_handshake connects no block, and in
process_message the single message is the whole iteration, so for
those two the non-IBD paths are out of reach entirely.
process_messages could reach them if one of its messages carried a
block building on the tip, but that is unlikely.

For process_message(s), leaving IBD used to be controllable from the
fuzz input via a jump_out_of_ibd bool that called JumpOutOfIbd().
Commit fa0a864b (#20908) dropped that toggle because mocktime made
it redundant: back then IsInitialBlockDownload() evaluated the tip
timestamp against the current mocked time on every call, so
SetMockTime(ConsumeTime(...)) alone could drive the node in and out
of IBD. That stopped working in #34253, which turned
IsInitialBlockDownload() into a lock-free read of the cached
m_cached_is_ibd flag, latched only by UpdateIBDStatus() on chain
activation; mocktime no longer affects it. p2p_handshake never had
such a toggle.

Restore the toggle. In process_message (a single message), the bool
is consumed last. In process_messages and p2p_handshake, the toggle
lives inside the message loop, as it did before fa0a864b. A latched
bool decides before each message whether to call JumpOutOfIbd(), so
some messages can be handled under IBD and the rest after leaving it.

Fixes: #34253
2026-07-22 11:21:56 +08:00

117 lines
4.3 KiB
C++

// Copyright (c) 2020-present 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 <banman.h>
#include <net.h>
#include <net_processing.h>
#include <protocol.h>
#include <sync.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/fuzz/util/net.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <test/util/validation.h>
#include <util/time.h>
#include <validationinterface.h>
#include <ios>
#include <utility>
#include <vector>
namespace {
TestingSetup* g_setup;
void initialize()
{
static const auto testing_setup = MakeNoLogFileContext<TestingSetup>(
/*chain_type=*/ChainType::REGTEST);
g_setup = testing_setup.get();
}
} // namespace
FUZZ_TARGET(p2p_handshake, .init = ::initialize)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
auto& node{g_setup->m_node};
auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)};
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
FakeNodeClock clock{1610000000s}; // 2021-01-07, arbitrary
FakeSteadyClock steady_clock;
chainman.ResetIbd();
node.banman.reset();
node.addrman.reset();
node.peerman.reset();
node.addrman = std::make_unique<AddrMan>(
*node.netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0);
node.peerman = PeerManager::make(connman, *node.addrman,
/*banman=*/nullptr, chainman,
*node.mempool, *node.warnings,
PeerManager::Options{
.reconcile_txs = true,
.deterministic_rng = true,
});
connman.SetMsgProc(node.peerman.get());
connman.SetAddrman(*node.addrman);
LOCK(NetEventsInterface::g_msgproc_mutex);
std::vector<CNode*> peers;
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
peers.push_back(ConsumeNodeAsUniquePtr(fuzzed_data_provider, steady_clock, i).release());
connman.AddTestNode(*peers.back());
node.peerman->InitializeNode(
*peers.back(),
static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()));
}
// Toggle IBD from within the loop, so that some messages may be processed
// under IBD and the rest after leaving it. JumpOutOfIbd() latches, so guard
// it to call at most once.
bool jump_out_of_ibd{false};
LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 100) {
CNode& connection = *PickValue(fuzzed_data_provider, peers);
if (connection.fDisconnect || connection.fSuccessfullyConnected) {
// Skip if the connection was disconnected or if the version
// handshake was already completed.
continue;
}
if (!jump_out_of_ibd) jump_out_of_ibd = fuzzed_data_provider.ConsumeBool();
if (jump_out_of_ibd && chainman.IsInitialBlockDownload()) chainman.JumpOutOfIbd();
clock += std::chrono::seconds{
fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(
-std::chrono::seconds{10min}.count(), // Allow mocktime to go backwards slightly
std::chrono::seconds{TIMEOUT_INTERVAL}.count()),
};
CSerializedNetMsg net_msg;
net_msg.m_type = PickValue(fuzzed_data_provider, ALL_NET_MESSAGE_TYPES);
net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH);
connman.FlushSendBuffer(connection);
(void)connman.ReceiveMsgFrom(connection, std::move(net_msg));
bool more_work{true};
while (more_work) {
connection.fPauseSend = false;
try {
more_work = connman.ProcessMessagesOnce(connection);
} catch (const std::ios_base::failure&) {
}
node.peerman->SendMessages(connection);
}
}
node.connman->StopNodes();
}