Skip to content

Commit 443c32a

Browse files
committed
Merge bitcoin/bitcoin#32822: fuzz: Make process_message(s) more deterministic
fa1a14a fuzz: Reset chainman state in process_message(s) targets (MarcoFalke) fa9a3de fuzz: DisableNextWrite (MarcoFalke) aeeeeec fuzz: Reset dirty connman state in process_message(s) targets (MarcoFalke) fa11eea fuzz: Avoid non-determinism in process_message(s) target (PeerMan) (MarcoFalke) Pull request description: `process_message(s)` are the least stable fuzz targets, according to OSS-Fuzz. Tracking issue: bitcoin/bitcoin#29018. ### Testing Needs coverage compilation, as explained in `./contrib/devtools/README.md`. And then, using 32 threads: ``` cargo run --manifest-path ./contrib/devtools/deterministic-fuzz-coverage/Cargo.toml -- $PWD/bld-cmake/ $PWD/../b-c-qa-assets/fuzz_corpora/ process_messages 32 ``` Each commit can be reverted to see more non-determinism re-appear. ACKs for top commit: marcofleon: ReACK fa1a14a dergoegge: reACK fa1a14a Tree-SHA512: 37b5b6dbdde6a39b4f83dc31e92cffb4a62a4b8f5befbf17029d943d0b2fd506f4a0833570dcdbf79a90b42af9caca44e98e838b03213d6bc1c3ecb70a6bb135
2 parents 5ad79b2 + fa1a14a commit 443c32a

File tree

8 files changed

+106
-21
lines changed

8 files changed

+106
-21
lines changed

src/test/fuzz/p2p_handshake.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ FUZZ_TARGET(p2p_handshake, .init = ::initialize)
4242
SeedRandomStateForTest(SeedRand::ZEROS);
4343
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
4444

45-
ConnmanTestMsg& connman = static_cast<ConnmanTestMsg&>(*g_setup->m_node.connman);
45+
auto& connman = static_cast<ConnmanTestMsg&>(*g_setup->m_node.connman);
4646
auto& chainman = static_cast<TestChainstateManager&>(*g_setup->m_node.chainman);
4747
SetMockTime(1610000000); // any time to successfully reset ibd
4848
chainman.ResetIbd();
4949

5050
node::Warnings warnings{};
5151
NetGroupManager netgroupman{{}};
52-
AddrMan addrman{netgroupman, /*deterministic=*/true, 0};
52+
AddrMan addrman{netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0};
5353
auto peerman = PeerManager::make(connman, addrman,
5454
/*banman=*/nullptr, chainman,
5555
*g_setup->m_node.mempool, warnings,

src/test/fuzz/process_message.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <consensus/consensus.h>
66
#include <net.h>
77
#include <net_processing.h>
8+
#include <node/warnings.h>
89
#include <primitives/transaction.h>
910
#include <protocol.h>
1011
#include <script/script.h>
@@ -29,8 +30,20 @@
2930
#include <vector>
3031

3132
namespace {
32-
const TestingSetup* g_setup;
33+
TestingSetup* g_setup;
3334
std::string_view LIMIT_TO_MESSAGE_TYPE{};
35+
36+
void ResetChainman(TestingSetup& setup)
37+
{
38+
SetMockTime(setup.m_node.chainman->GetParams().GenesisBlock().Time());
39+
setup.m_node.chainman.reset();
40+
setup.m_make_chainman();
41+
setup.LoadVerifyActivateChainstate();
42+
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
43+
MineBlock(setup.m_node, {});
44+
}
45+
setup.m_node.validation_signals->SyncWithValidationInterfaceQueue();
46+
}
3447
} // namespace
3548

3649
void initialize_process_message()
@@ -40,27 +53,41 @@ void initialize_process_message()
4053
Assert(std::count(ALL_NET_MESSAGE_TYPES.begin(), ALL_NET_MESSAGE_TYPES.end(), LIMIT_TO_MESSAGE_TYPE)); // Unknown message type passed
4154
}
4255

43-
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(
56+
static const auto testing_setup{
57+
MakeNoLogFileContext<TestingSetup>(
4458
/*chain_type=*/ChainType::REGTEST,
45-
{.extra_args = {"-txreconciliation"}});
59+
{}),
60+
};
4661
g_setup = testing_setup.get();
47-
SetMockTime(WITH_LOCK(g_setup->m_node.chainman->GetMutex(), return g_setup->m_node.chainman->ActiveTip()->Time()));
48-
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
49-
MineBlock(g_setup->m_node, {});
50-
}
51-
g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
62+
ResetChainman(*g_setup);
5263
}
5364

5465
FUZZ_TARGET(process_message, .init = initialize_process_message)
5566
{
5667
SeedRandomStateForTest(SeedRand::ZEROS);
5768
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
5869

59-
ConnmanTestMsg& connman = *static_cast<ConnmanTestMsg*>(g_setup->m_node.connman.get());
70+
auto& connman = static_cast<ConnmanTestMsg&>(*g_setup->m_node.connman);
71+
connman.ResetAddrCache();
72+
connman.ResetMaxOutboundCycle();
6073
auto& chainman = static_cast<TestChainstateManager&>(*g_setup->m_node.chainman);
74+
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
6175
SetMockTime(1610000000); // any time to successfully reset ibd
6276
chainman.ResetIbd();
77+
chainman.DisableNextWrite();
6378

79+
node::Warnings warnings{};
80+
NetGroupManager netgroupman{{}};
81+
AddrMan addrman{netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0};
82+
auto peerman = PeerManager::make(connman, addrman,
83+
/*banman=*/nullptr, chainman,
84+
*g_setup->m_node.mempool, warnings,
85+
PeerManager::Options{
86+
.reconcile_txs = true,
87+
.deterministic_rng = true,
88+
});
89+
90+
connman.SetMsgProc(peerman.get());
6491
LOCK(NetEventsInterface::g_msgproc_mutex);
6592

6693
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::MESSAGE_TYPE_SIZE).c_str()};
@@ -93,4 +120,8 @@ FUZZ_TARGET(process_message, .init = initialize_process_message)
93120
}
94121
g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
95122
g_setup->m_node.connman->StopNodes();
123+
if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) {
124+
// Reuse the global chainman, but reset it when it is dirty
125+
ResetChainman(*g_setup);
126+
}
96127
}

src/test/fuzz/process_messages.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <consensus/consensus.h>
66
#include <net.h>
77
#include <net_processing.h>
8+
#include <node/warnings.h>
89
#include <protocol.h>
910
#include <script/script.h>
1011
#include <sync.h>
@@ -25,31 +26,57 @@
2526
#include <vector>
2627

2728
namespace {
28-
const TestingSetup* g_setup;
29+
TestingSetup* g_setup;
30+
31+
void ResetChainman(TestingSetup& setup)
32+
{
33+
SetMockTime(setup.m_node.chainman->GetParams().GenesisBlock().Time());
34+
setup.m_node.chainman.reset();
35+
setup.m_make_chainman();
36+
setup.LoadVerifyActivateChainstate();
37+
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
38+
MineBlock(setup.m_node, {});
39+
}
40+
setup.m_node.validation_signals->SyncWithValidationInterfaceQueue();
41+
}
2942
} // namespace
3043

3144
void initialize_process_messages()
3245
{
33-
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>(
46+
static const auto testing_setup{
47+
MakeNoLogFileContext<TestingSetup>(
3448
/*chain_type=*/ChainType::REGTEST,
35-
{.extra_args = {"-txreconciliation"}});
49+
{}),
50+
};
3651
g_setup = testing_setup.get();
37-
SetMockTime(WITH_LOCK(g_setup->m_node.chainman->GetMutex(), return g_setup->m_node.chainman->ActiveTip()->Time()));
38-
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
39-
MineBlock(g_setup->m_node, {});
40-
}
41-
g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
52+
ResetChainman(*g_setup);
4253
}
4354

4455
FUZZ_TARGET(process_messages, .init = initialize_process_messages)
4556
{
4657
SeedRandomStateForTest(SeedRand::ZEROS);
4758
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
4859

49-
ConnmanTestMsg& connman = *static_cast<ConnmanTestMsg*>(g_setup->m_node.connman.get());
60+
auto& connman = static_cast<ConnmanTestMsg&>(*g_setup->m_node.connman);
61+
connman.ResetAddrCache();
62+
connman.ResetMaxOutboundCycle();
5063
auto& chainman = static_cast<TestChainstateManager&>(*g_setup->m_node.chainman);
64+
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
5165
SetMockTime(1610000000); // any time to successfully reset ibd
5266
chainman.ResetIbd();
67+
chainman.DisableNextWrite();
68+
69+
node::Warnings warnings{};
70+
NetGroupManager netgroupman{{}};
71+
AddrMan addrman{netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0};
72+
auto peerman = PeerManager::make(connman, addrman,
73+
/*banman=*/nullptr, chainman,
74+
*g_setup->m_node.mempool, warnings,
75+
PeerManager::Options{
76+
.reconcile_txs = true,
77+
.deterministic_rng = true,
78+
});
79+
connman.SetMsgProc(peerman.get());
5380

5481
LOCK(NetEventsInterface::g_msgproc_mutex);
5582

@@ -93,4 +120,8 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages)
93120
}
94121
g_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue();
95122
g_setup->m_node.connman->StopNodes();
123+
if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) {
124+
// Reuse the global chainman, but reset it when it is dirty
125+
ResetChainman(*g_setup);
126+
}
96127
}

src/test/util/net.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ void ConnmanTestMsg::Handshake(CNode& node,
7171
}
7272
}
7373

74+
void ConnmanTestMsg::ResetAddrCache() { m_addr_response_caches = {}; }
75+
76+
void ConnmanTestMsg::ResetMaxOutboundCycle()
77+
{
78+
LOCK(m_total_bytes_sent_mutex);
79+
nMaxOutboundCycleStartTime = 0s;
80+
nMaxOutboundTotalBytesSentInCycle = 0;
81+
}
82+
7483
void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, std::span<const uint8_t> msg_bytes, bool& complete) const
7584
{
7685
assert(node.ReceiveMsgBytes(msg_bytes, complete));

src/test/util/net.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ struct ConnmanTestMsg : public CConnman {
4545
m_peer_connect_timeout = timeout;
4646
}
4747

48+
void ResetAddrCache();
49+
void ResetMaxOutboundCycle();
50+
4851
std::vector<CNode*> TestNodes()
4952
{
5053
LOCK(m_nodes_mutex);

src/test/util/validation.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
#include <validation.h>
1010
#include <validationinterface.h>
1111

12+
void TestChainstateManager::DisableNextWrite()
13+
{
14+
struct TestChainstate : public Chainstate {
15+
void ResetNextWrite() { m_next_write = NodeClock::time_point::max() - 1s; }
16+
};
17+
for (auto* cs : GetAll()) {
18+
static_cast<TestChainstate*>(cs)->ResetNextWrite();
19+
}
20+
}
1221
void TestChainstateManager::ResetIbd()
1322
{
1423
m_cached_finished_ibd = false;

src/test/util/validation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
class CValidationInterface;
1111

1212
struct TestChainstateManager : public ChainstateManager {
13+
/** Disable the next write of all chainstates */
14+
void DisableNextWrite();
1315
/** Reset the ibd cache to its initial state */
1416
void ResetIbd();
1517
/** Toggle IsInitialBlockDownload from true to false */

src/validation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ class Chainstate
785785
return m_mempool ? &m_mempool->cs : nullptr;
786786
}
787787

788-
private:
788+
protected:
789789
bool ActivateBestChainStep(BlockValidationState& state, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
790790
bool ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool->cs);
791791

0 commit comments

Comments
 (0)