From 801d36f55b6d421f26d2376327699a04bd22e602 Mon Sep 17 00:00:00 2001 From: Eugene Siegel Date: Wed, 13 May 2026 12:00:27 -0400 Subject: [PATCH] fuzz: use ImmediateBackgroundTaskRunner to silence DEBUG_LOCKORDER DEBUG_LOCKORDER was reporting a false positive deadlock with the cmpctblock fuzz harness when using ImmediateTaskRunner. Since it is single-threaded, ImmediateTaskRunner callbacks added LockOrders that could never happen outside of a fuzz test. First a block would get connected: * LOCK(mempool.cs) * BlockConnected (fuzz test runs in same thread) * LOCK(m_tx_download_mutex) Then a later iteration of the LIMITED_WHILE would send a TX: * LOCK(m_tx_download_mutex) * LOCK(mempool.cs) causing a false positive deadlock. Normally, the BlockConnected callback would run in a different thread and no deadlock is reported. Fix this by launching a thread that runs the callback and is immediately joined. --- src/test/fuzz/cmpctblock.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/fuzz/cmpctblock.cpp b/src/test/fuzz/cmpctblock.cpp index 3e4268cb658..6af9883a2e9 100644 --- a/src/test/fuzz/cmpctblock.cpp +++ b/src/test/fuzz/cmpctblock.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include @@ -135,6 +137,15 @@ void ResetChainmanAndMempool(TestingSetup& setup) } } +//! Used to run tasks in a std::thread to avoid DEBUG_LOCKORDER false positives. +class ImmediateBackgroundTaskRunner : public util::TaskRunnerInterface +{ +public: + void insert(std::function func) override { std::thread(std::move(func)).join(); } + void flush() override {} + size_t size() override { return 0; } +}; + } // namespace extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept; @@ -144,6 +155,8 @@ void initialize_cmpctblock() static const auto testing_setup = MakeNoLogFileContext(); g_setup = testing_setup.get(); g_nBits = Params().GenesisBlock().nBits; + // Replace validation_signals before creating chainman and mempool so they use it. + testing_setup->m_node.validation_signals = std::make_unique(std::make_unique()); ResetChainmanAndMempool(*g_setup); }