validation: Don't add pruned blocks to m_blocks_unlinked on startup
What changed, and why it matters
This commit fixes a bug where Bitcoin Core could crash on startup with a failed internal consistency check. The crash happened when a node had 'pruned' old block data while also having some header-only blocks, causing the program to incorrectly re-add pruned blocks to an internal list that is supposed to contain only blocks still stored on disk. The fix adds a check to skip pruned blocks when rebuilding that list during startup.
Apply the patch. It is a minimal, targeted fix. Nodes that prune and have header-only parent blocks in their block index are at risk of failing to start; upgrading or backporting is advisable. No immediate remote exploitation vector is evident, but the local crash-on-startup condition is disruptive.
Security signals we found
Assertion failure / denial of service at startup
Inconsistent internal block index state
Pruning interaction with header-only parent blocks
Fixes reported issue #35050
Evidence from the diff
In BlockManager::LoadBlockIndex(), the code builds m_blocks_unlinked for blocks whose parent is not yet known. Previously it inserted entries based solely on nTx > 0 without verifying BLOCK_HAVE_DATA. Pruning preserves nTx but clears BLOCK_HAVE_DATA, so a pruned block whose parent was header-only would be inserted into m_blocks_unlinked on every restart. This violates the CheckBlockIndex() invariant that entries in m_blocks_unlinked must have data on disk, leading to an assertion failure and node crash at startup. The patch guards the insertion with a BLOCK_HAVE_DATA check.
Changed components
src/node/blockstorage.cppBlockManager::LoadBlockIndex()m_blocks_unlinkedCheckBlockIndex() consistency checkInspect captured patch +3 / −1
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index b0842a00..411f0ca8 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -487,7 +487,9 @@ bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockha
pindex->m_chain_tx_count = pindex->pprev->m_chain_tx_count + pindex->nTx;
} else {
pindex->m_chain_tx_count = 0;
- m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
+ if (pindex->nStatus & BLOCK_HAVE_DATA) {
+ m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
+ }
}
} else {
pindex->m_chain_tx_count = pindex->nTx;
Why this scored 53/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.