mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 05:32:22 +02:00
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(). Commitfa0a864b(#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 beforefa0a864b. 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
117 lines
4.3 KiB
C++
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();
|
|
}
|