mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 05:32:22 +02:00
The mempool is reused across iterations of process_message and process_messages, but it was never reset, so a transaction accepted into the mempool by one input would leak into the next iteration. A random payload can produce a transaction that passes AcceptToMemoryPool. Rebuild the mempool together with the chainman in ResetChainmanAndMempool(), called when either the block index grew or the mempool was modified. The mempool is bound to the chainman at chainman construction (ChainstateLoadOptions::mempool), so the two must be reset together: the mempool is rebuilt first, then the chainman, which re-binds the fresh mempool. This mirrors the cmpctblock harness.
142 lines
5.3 KiB
C++
142 lines
5.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 <addrman.h>
|
|
#include <banman.h>
|
|
#include <kernel/chainparams.h>
|
|
#include <net.h>
|
|
#include <net_processing.h>
|
|
#include <primitives/block.h>
|
|
#include <primitives/transaction.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/random.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <test/util/time.h>
|
|
#include <test/util/validation.h>
|
|
#include <uint256.h>
|
|
#include <util/check.h>
|
|
#include <util/time.h>
|
|
#include <validation.h>
|
|
#include <validationinterface.h>
|
|
|
|
#include <functional>
|
|
#include <ios>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
TestingSetup* g_setup;
|
|
|
|
} // namespace
|
|
|
|
extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
|
|
|
|
void initialize_process_messages()
|
|
{
|
|
static const auto testing_setup{
|
|
MakeNoLogFileContext<TestingSetup>(
|
|
/*chain_type=*/ChainType::REGTEST,
|
|
{}),
|
|
};
|
|
g_setup = testing_setup.get();
|
|
ResetChainmanAndMempool(*g_setup);
|
|
}
|
|
|
|
FUZZ_TARGET(process_messages, .init = initialize_process_messages)
|
|
{
|
|
SeedRandomStateForTest(SeedRand::ZEROS);
|
|
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
|
|
|
|
auto& node{g_setup->m_node};
|
|
auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)};
|
|
connman.Reset();
|
|
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
|
|
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
|
|
const auto initial_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())};
|
|
GetFakeNodeClock().set(1610000000s); // 2021-01-07, arbitrary
|
|
FakeSteadyClock steady_clock;
|
|
chainman.ResetIbd();
|
|
chainman.DisableNextWrite();
|
|
|
|
// Reset, so that dangling pointers can be detected by sanitizers.
|
|
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);
|
|
|
|
node.validation_signals->RegisterValidationInterface(node.peerman.get());
|
|
|
|
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());
|
|
CNode& p2p_node = *peers.back();
|
|
|
|
FillNode(fuzzed_data_provider, connman, p2p_node);
|
|
|
|
connman.AddTestNode(p2p_node);
|
|
}
|
|
|
|
// 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(), 30) {
|
|
if (!jump_out_of_ibd) jump_out_of_ibd = fuzzed_data_provider.ConsumeBool();
|
|
if (jump_out_of_ibd && chainman.IsInitialBlockDownload()) chainman.JumpOutOfIbd();
|
|
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::MESSAGE_TYPE_SIZE).c_str()};
|
|
|
|
GetFakeNodeClock().set(ConsumeTime(fuzzed_data_provider));
|
|
|
|
CSerializedNetMsg net_msg;
|
|
net_msg.m_type = random_message_type;
|
|
net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH);
|
|
|
|
CNode& random_node = *PickValue(fuzzed_data_provider, peers);
|
|
|
|
connman.FlushSendBuffer(random_node);
|
|
(void)connman.ReceiveMsgFrom(random_node, std::move(net_msg));
|
|
|
|
bool more_work{true};
|
|
while (more_work) { // Ensure that every message is eventually processed in some way or another
|
|
random_node.fPauseSend = false;
|
|
|
|
try {
|
|
more_work = connman.ProcessMessagesOnce(random_node);
|
|
} catch (const std::ios_base::failure&) {
|
|
}
|
|
node.peerman->SendMessages(random_node);
|
|
}
|
|
}
|
|
node.validation_signals->SyncWithValidationInterfaceQueue();
|
|
node.validation_signals->UnregisterValidationInterface(node.peerman.get());
|
|
node.connman->StopNodes();
|
|
const auto end_sequence{WITH_LOCK(node.mempool->cs, return node.mempool->GetSequence())};
|
|
if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size()) || initial_sequence != end_sequence) {
|
|
// Reuse the global chainman and mempool, but reset them when dirty.
|
|
MakeRandDeterministicDANGEROUS(uint256::ZERO);
|
|
ResetChainmanAndMempool(*g_setup);
|
|
}
|
|
}
|