validation: check invariants when inserting into m_blocks_unlinked

For an entry A -> B in m_blocks_unlinked, the entry B was added into
m_blocks_unlinked either because:
- some ancestor of B was never received (or)
- some ancestor of B was pruned away.

Every insert must satisfy two invariants:
1. B has BLOCK_HAVE_DATA set.
2. No duplicate A -> B entries in m_blocks_unlinked (this is UB zone if
	this entry gets popped twice in ReceivedBlockTransactions and
	happens to be in setBlockIndexCandidates)

2 bugs (#35070 and #35168) discovered recently stemmed from the
m_blocks_unlinked insertion sites not enforcing these invariants.
So add a helper which wraps around insertion sites of m_blocks_unlinked
with these invariants.

Co-authored-by: Martin Zumsande <mzumsande@gmail.com>
This commit is contained in:
stratospher
2026-05-30 01:01:40 +05:30
parent 0852925bd8
commit d6359937bf
3 changed files with 16 additions and 6 deletions

View File

@@ -255,6 +255,18 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, CBlockInde
return pindexNew;
}
void BlockManager::AddUnlinkedBlock(CBlockIndex* block)
{
AssertLockHeld(cs_main);
Assume(block != nullptr);
Assume(block->nStatus & BLOCK_HAVE_DATA);
auto range = m_blocks_unlinked.equal_range(block->pprev);
for (auto it = range.first; it != range.second; ++it) {
if (it->second == block) return; // don't insert duplicates
}
m_blocks_unlinked.emplace(block->pprev, block);
}
void BlockManager::PruneOneBlockFile(const int fileNumber)
{
AssertLockHeld(cs_main);
@@ -487,7 +499,7 @@ bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockha
} else {
pindex->m_chain_tx_count = 0;
if (pindex->nStatus & BLOCK_HAVE_DATA) {
m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
AddUnlinkedBlock(pindex);
}
}
} else {

View File

@@ -354,6 +354,7 @@ public:
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
*/
std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
void AddUnlinkedBlock(CBlockIndex* block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
std::unique_ptr<BlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);