From 87b080fe2b66036184a54a0bfc320498dd416d74 Mon Sep 17 00:00:00 2001 From: Hao Xu Date: Fri, 19 Jun 2026 20:31:11 +0800 Subject: [PATCH] fuzz: reset the reused mempool in process_message(s) 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. --- src/test/fuzz/process_message.cpp | 25 ++++++------------------- src/test/fuzz/process_messages.cpp | 26 +++++++------------------- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp index b35c4021ae0..e4f0a36dbee 100644 --- a/src/test/fuzz/process_message.cpp +++ b/src/test/fuzz/process_message.cpp @@ -4,11 +4,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -17,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -45,17 +42,6 @@ namespace { TestingSetup* g_setup; std::string_view LIMIT_TO_MESSAGE_TYPE{}; -void ResetChainman(TestingSetup& setup) -{ - GetFakeNodeClock().set(setup.m_node.chainman->GetParams().GenesisBlock().Time()); - setup.m_node.chainman.reset(); - setup.m_make_chainman(); - setup.LoadVerifyActivateChainstate(); - for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { - node::BlockCreateOptions options; - MineBlock(setup.m_node, options); - } -} } // namespace extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept; @@ -73,7 +59,7 @@ void initialize_process_message() {}), }; g_setup = testing_setup.get(); - ResetChainman(*g_setup); + ResetChainmanAndMempool(*g_setup); } FUZZ_TARGET(process_message, .init = initialize_process_message) @@ -86,6 +72,7 @@ FUZZ_TARGET(process_message, .init = initialize_process_message) connman.Reset(); auto& chainman{static_cast(*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(); @@ -145,10 +132,10 @@ FUZZ_TARGET(process_message, .init = initialize_process_message) node.validation_signals->SyncWithValidationInterfaceQueue(); node.validation_signals->UnregisterValidationInterface(node.peerman.get()); node.connman->StopNodes(); - if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) { - // Reuse the global chainman, but reset it when it is dirty. Reset the - // rng first, so ResetChainman() consumes it from a fixed seed. + 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); - ResetChainman(*g_setup); + ResetChainmanAndMempool(*g_setup); } } diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp index 9a1f28af92c..983d2a5e14b 100644 --- a/src/test/fuzz/process_messages.cpp +++ b/src/test/fuzz/process_messages.cpp @@ -4,11 +4,9 @@ #include #include -#include #include #include #include -#include #include #include #include @@ -17,13 +15,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -39,17 +37,6 @@ namespace { TestingSetup* g_setup; -void ResetChainman(TestingSetup& setup) -{ - GetFakeNodeClock().set(setup.m_node.chainman->GetParams().GenesisBlock().Time()); - setup.m_node.chainman.reset(); - setup.m_make_chainman(); - setup.LoadVerifyActivateChainstate(); - node::BlockCreateOptions options; - for (int i = 0; i < 2 * COINBASE_MATURITY; i++) { - MineBlock(setup.m_node, options); - } -} } // namespace extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept; @@ -62,7 +49,7 @@ void initialize_process_messages() {}), }; g_setup = testing_setup.get(); - ResetChainman(*g_setup); + ResetChainmanAndMempool(*g_setup); } FUZZ_TARGET(process_messages, .init = initialize_process_messages) @@ -75,6 +62,7 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages) connman.Reset(); auto& chainman{static_cast(*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(); @@ -144,10 +132,10 @@ FUZZ_TARGET(process_messages, .init = initialize_process_messages) node.validation_signals->SyncWithValidationInterfaceQueue(); node.validation_signals->UnregisterValidationInterface(node.peerman.get()); node.connman->StopNodes(); - if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) { - // Reuse the global chainman, but reset it when it is dirty. Reset the - // rng first, so ResetChainman() consumes it from a fixed seed. + 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); - ResetChainman(*g_setup); + ResetChainmanAndMempool(*g_setup); } }