Skip to content

Commit 36c201f

Browse files
committed
remove CBlockIndex copy construction
Copy construction of CBlockIndex objects is a footgun because of the wide use of equality-by-pointer comparison in the code base. There are also potential lifetime confusions of using copied instances, since there are recursive pointer references (e.g. pprev). We can't just delete the copy constructors because they are used for derived classes (CDiskBlockIndex), so we mark them protected. Delete move constructors and declare the destructor to satisfy the "rule of 5."
1 parent fc44d17 commit 36c201f

File tree

3 files changed

+31
-17
lines changed

3 files changed

+31
-17
lines changed

src/chain.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,6 @@ class CBlockIndex
213213
//! (memory only) Maximum nTime in the chain up to and including this block.
214214
unsigned int nTimeMax{0};
215215

216-
CBlockIndex()
217-
{
218-
}
219-
220216
explicit CBlockIndex(const CBlockHeader& block)
221217
: nVersion{block.nVersion},
222218
hashMerkleRoot{block.hashMerkleRoot},
@@ -355,6 +351,24 @@ class CBlockIndex
355351
//! Efficiently find an ancestor of this block.
356352
CBlockIndex* GetAncestor(int height);
357353
const CBlockIndex* GetAncestor(int height) const;
354+
355+
CBlockIndex() = default;
356+
~CBlockIndex() = default;
357+
358+
protected:
359+
//! CBlockIndex should not allow public copy construction because equality
360+
//! comparison via pointer is very common throughout the codebase, making
361+
//! use of copy a footgun. Also, use of copies do not have the benefit
362+
//! of simplifying lifetime considerations due to attributes like pprev and
363+
//! pskip, which are at risk of becoming dangling pointers in a copied
364+
//! instance.
365+
//!
366+
//! We declare these protected instead of simply deleting them so that
367+
//! CDiskBlockIndex can reuse copy construction.
368+
CBlockIndex(const CBlockIndex&) = default;
369+
CBlockIndex& operator=(const CBlockIndex&) = delete;
370+
CBlockIndex(CBlockIndex&&) = delete;
371+
CBlockIndex& operator=(CBlockIndex&&) = delete;
358372
};
359373

360374
arith_uint256 GetBlockProof(const CBlockIndex& block);

src/test/fuzz/pow.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,18 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
2525
{
2626
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
2727
const Consensus::Params& consensus_params = Params().GetConsensus();
28-
std::vector<CBlockIndex> blocks;
28+
std::vector<std::unique_ptr<CBlockIndex>> blocks;
2929
const uint32_t fixed_time = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
3030
const uint32_t fixed_bits = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
3131
LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 10000) {
3232
const std::optional<CBlockHeader> block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
3333
if (!block_header) {
3434
continue;
3535
}
36-
CBlockIndex current_block{*block_header};
36+
CBlockIndex& current_block{
37+
*blocks.emplace_back(std::make_unique<CBlockIndex>(*block_header))};
3738
{
38-
CBlockIndex* previous_block = blocks.empty() ? nullptr : &PickValue(fuzzed_data_provider, blocks);
39+
CBlockIndex* previous_block = blocks.empty() ? nullptr : PickValue(fuzzed_data_provider, blocks).get();
3940
const int current_height = (previous_block != nullptr && previous_block->nHeight != std::numeric_limits<int>::max()) ? previous_block->nHeight + 1 : 0;
4041
if (fuzzed_data_provider.ConsumeBool()) {
4142
current_block.pprev = previous_block;
@@ -57,7 +58,6 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
5758
} else {
5859
current_block.nChainWork = ConsumeArithUInt256(fuzzed_data_provider);
5960
}
60-
blocks.push_back(current_block);
6161
}
6262
{
6363
(void)GetBlockProof(current_block);
@@ -67,9 +67,9 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
6767
}
6868
}
6969
{
70-
const CBlockIndex* to = &PickValue(fuzzed_data_provider, blocks);
71-
const CBlockIndex* from = &PickValue(fuzzed_data_provider, blocks);
72-
const CBlockIndex* tip = &PickValue(fuzzed_data_provider, blocks);
70+
const auto& to = PickValue(fuzzed_data_provider, blocks);
71+
const auto& from = PickValue(fuzzed_data_provider, blocks);
72+
const auto& tip = PickValue(fuzzed_data_provider, blocks);
7373
try {
7474
(void)GetBlockProofEquivalentTime(*to, *from, *tip, consensus_params);
7575
} catch (const uint_error&) {

src/test/miner_tests.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ constexpr static struct {
7878
{0, 792342903}, {6, 678455063}, {6, 773251385}, {5, 186617471}, {6, 883189502}, {7, 396077336},
7979
{8, 254702874}, {0, 455592851}};
8080

81-
static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
81+
static std::unique_ptr<CBlockIndex> CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
8282
{
83-
CBlockIndex index;
84-
index.nHeight = nHeight;
85-
index.pprev = active_chain_tip;
83+
auto index{std::make_unique<CBlockIndex>()};
84+
index->nHeight = nHeight;
85+
index->pprev = active_chain_tip;
8686
return index;
8787
}
8888

@@ -394,7 +394,7 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
394394

395395
{
396396
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
397-
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
397+
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, *CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
398398
}
399399

400400
// relative time locked
@@ -411,7 +411,7 @@ void MinerTestingSetup::TestBasicMining(const CChainParams& chainparams, const C
411411
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
412412
{
413413
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
414-
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip)));
414+
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, *CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip)));
415415
}
416416

417417
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; ++i) {

0 commit comments

Comments
 (0)