Change CChain::FindFork() to take ref
What changed, and why it matters
This is a small internal code cleanup in Bitcoin Core. It changes one function, CChain::FindFork(), so that callers must pass a valid block object directly rather than a pointer that might be null. The function previously handled null pointers safely by returning null; now callers must check for null themselves before calling it. The commit adds those checks at every call site. It is a defensive refactoring, not a fix for an active security bug.
Treat as routine refactoring. Reviewers should verify that every call site now correctly guards against null before dereferencing. No urgent action is required; no patch or advisory is indicated by the supplied materials.
Security signals we found
Refactoring removes internal null guard and pushes null checks to callers
One call site (rpc/blockchain.cpp getchaintips) adds CHECK_NONFATAL guard on a previously unguarded pointer
No memory corruption, consensus, cryptography, or network changes present
No vendor security disclosure or CVE references in commit or supplied materials
Evidence from the diff
CChain::FindFork() is refactored from taking const CBlockIndex* to const CBlockIndex&, removing the internal null check. Call sites are updated to dereference pointers after verifying non-nullity, or in one RPC case using CHECK_NONFATAL. The change makes null-dereference inside FindFork() impossible by construction, shifting the responsibility for null handling to callers. No behavior change is intended for valid inputs.
Changed components
src/chain.cpp / CChain::FindFork()src/index/base.cppsrc/init.cppsrc/rpc/blockchain.cpp (getchaintips)src/validation.cppsrc/test/chain_tests.cppsrc/test/fuzz/block_index_tree.cppInspect captured patch +14 / −14
diff --git a/src/chain.cpp b/src/chain.cpp
index 958872e5..b066f0f7 100644
--- a/src/chain.cpp
+++ b/src/chain.cpp
@@ -47,10 +47,9 @@ CBlockLocator GetLocator(const CBlockIndex* index)
return CBlockLocator{LocatorEntries(index)};
}
-const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
- if (pindex == nullptr) {
- return nullptr;
- }
+const CBlockIndex* CChain::FindFork(const CBlockIndex& index) const
+{
+ const auto* pindex{&index};
if (pindex->nHeight > Height())
pindex = pindex->GetAncestor(Height());
while (pindex && !Contains(*pindex))
diff --git a/src/chain.h b/src/chain.h
index a2295b08..7701e926 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -440,7 +440,7 @@ public:
void SetTip(CBlockIndex& block);
/** Find the last common block between this chain and a block index entry. */
- const CBlockIndex* FindFork(const CBlockIndex* pindex) const;
+ const CBlockIndex* FindFork(const CBlockIndex& index) const;
/** Find the earliest block with timestamp equal or greater than the given time and height equal or greater than the given height. */
CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
diff --git a/src/index/base.cpp b/src/index/base.cpp
index 416137dd..bb41cc2a 100644
--- a/src/index/base.cpp
+++ b/src/index/base.cpp
@@ -166,7 +166,7 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* const pindex_prev, CC
// Since block is not in the chain, return the next block in the chain AFTER the last common ancestor.
// Caller will be responsible for rewinding back to the common ancestor.
- const auto* fork{chain.FindFork(pindex_prev)};
+ const auto* fork{chain.FindFork(*pindex_prev)};
// Common ancestor must exist (genesis).
return chain.Next(*Assert(fork));
}
diff --git a/src/init.cpp b/src/init.cpp
index 6642c87f..aa153a27 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -2374,7 +2374,7 @@ bool StartIndexBackgroundSync(NodeContext& node)
LogWarning("Failed to find block manager entry for best block %s from %s, falling back to genesis for index sync",
summary.best_block_hash.ToString(), summary.name);
} else if (!index_chain.Contains(*pindex)) {
- pindex = index_chain.FindFork(pindex);
+ pindex = index_chain.FindFork(*pindex);
}
}
if (!pindex) {
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 956f7066..d65b672f 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -1649,11 +1649,12 @@ static RPCMethod getchaintips()
/* Construct the output array. */
UniValue res(UniValue::VARR);
for (const CBlockIndex* block : setTips) {
+ CHECK_NONFATAL(block);
UniValue obj(UniValue::VOBJ);
obj.pushKV("height", block->nHeight);
obj.pushKV("hash", block->phashBlock->GetHex());
- const int branchLen = block->nHeight - active_chain.FindFork(block)->nHeight;
+ const int branchLen = block->nHeight - active_chain.FindFork(*block)->nHeight;
obj.pushKV("branchlen", branchLen);
std::string status;
diff --git a/src/test/chain_tests.cpp b/src/test/chain_tests.cpp
index cf620958..966802b6 100644
--- a/src/test/chain_tests.cpp
+++ b/src/test/chain_tests.cpp
@@ -96,13 +96,13 @@ BOOST_AUTO_TEST_CASE(findfork_tests)
const auto check_same{[](const CChain& chain, const auto& blocks) {
for (const auto& block : blocks) {
- BOOST_CHECK_EQUAL(chain.FindFork(&block), &block);
+ BOOST_CHECK_EQUAL(chain.FindFork(block), &block);
}
}};
const auto check_fork_point{[](const CChain& chain, const auto& blocks, const CBlockIndex& fork_point) {
for (const auto& block : blocks) {
- BOOST_CHECK_EQUAL(chain.FindFork(&block), &fork_point);
+ BOOST_CHECK_EQUAL(chain.FindFork(block), &fork_point);
}
}};
@@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(findfork_tests)
// Invalid test case. Mixing chains is not supported
CBlockIndex block_on_unrelated_chain;
- BOOST_CHECK_EQUAL(chain_long.FindFork(&block_on_unrelated_chain), nullptr);
+ BOOST_CHECK_EQUAL(chain_long.FindFork(block_on_unrelated_chain), nullptr);
}
BOOST_AUTO_TEST_CASE(chain_test)
diff --git a/src/test/fuzz/block_index_tree.cpp b/src/test/fuzz/block_index_tree.cpp
index 0e333cd6..2eac6d6b 100644
--- a/src/test/fuzz/block_index_tree.cpp
+++ b/src/test/fuzz/block_index_tree.cpp
@@ -102,7 +102,7 @@ FUZZ_TARGET(block_index_tree, .init = initialize_block_index_tree)
assert(best_tip); // Should at least return current tip
if (best_tip == chain.Tip()) break; // Nothing to do
// Rewind chain to forking point
- const CBlockIndex* fork = chain.FindFork(best_tip);
+ const CBlockIndex* fork = chain.FindFork(*best_tip);
// If we can't go back to the fork point due to pruned data, abort this run. In reality, a pruned node would also currently just crash in this scenario.
// This is very unlikely to happen due to the minimum pruning threshold of 550MiB.
CBlockIndex* it = chain.Tip();
diff --git a/src/validation.cpp b/src/validation.cpp
index b8903e81..d57997ce 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3183,7 +3183,7 @@ bool Chainstate::ActivateBestChainStep(BlockValidationState& state, CBlockIndex*
if (m_mempool) AssertLockHeld(m_mempool->cs);
const CBlockIndex* pindexOldTip = m_chain.Tip();
- const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork);
+ const CBlockIndex* pindexFork = pindexMostWork ? m_chain.FindFork(*pindexMostWork) : nullptr;
// Disconnect active blocks which are no longer in the best chain.
bool fBlocksDisconnected = false;
@@ -3397,7 +3397,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
} while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip)));
if (!blocks_connected) return true;
- const CBlockIndex* pindexFork = m_chain.FindFork(starting_tip);
+ const CBlockIndex* pindexFork = starting_tip ? m_chain.FindFork(*starting_tip) : nullptr;
bool still_in_ibd = m_chainman.IsInitialBlockDownload();
if (was_in_ibd && !still_in_ibd) {
Why this scored 18/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.