Skip to content

Commit 65f5cfd

Browse files
committed
Merge bitcoin/bitcoin#25311: refactor: remove CBlockIndex copy construction
36c201f remove CBlockIndex copy construction (James O'Beirne) Pull request description: 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 members (e.g. pprev). (See also bitcoin/bitcoin#24008 (comment)) We can't just delete the copy constructors because they are used for derived classes (CDiskBlockIndex), so we mark them protected. ACKs for top commit: ajtowns: ACK 36c201f - code review only MarcoFalke: re-ACK 36c201f 🏻 Tree-SHA512: b1cf9a1cb992464a4377dad609713eea63cc099435df374e4553bfe62d362a4eb5e3c6c6649177832f38c0905b23841caf9d62196cef8e3084bfea0bfc26374b
2 parents bd13d6b + 36c201f commit 65f5cfd

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
@@ -26,17 +26,18 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
2626
{
2727
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
2828
const Consensus::Params& consensus_params = Params().GetConsensus();
29-
std::vector<CBlockIndex> blocks;
29+
std::vector<std::unique_ptr<CBlockIndex>> blocks;
3030
const uint32_t fixed_time = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
3131
const uint32_t fixed_bits = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
3232
LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 10000) {
3333
const std::optional<CBlockHeader> block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
3434
if (!block_header) {
3535
continue;
3636
}
37-
CBlockIndex current_block{*block_header};
37+
CBlockIndex& current_block{
38+
*blocks.emplace_back(std::make_unique<CBlockIndex>(*block_header))};
3839
{
39-
CBlockIndex* previous_block = blocks.empty() ? nullptr : &PickValue(fuzzed_data_provider, blocks);
40+
CBlockIndex* previous_block = blocks.empty() ? nullptr : PickValue(fuzzed_data_provider, blocks).get();
4041
const int current_height = (previous_block != nullptr && previous_block->nHeight != std::numeric_limits<int>::max()) ? previous_block->nHeight + 1 : 0;
4142
if (fuzzed_data_provider.ConsumeBool()) {
4243
current_block.pprev = previous_block;
@@ -58,7 +59,6 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
5859
} else {
5960
current_block.nChainWork = ConsumeArithUInt256(fuzzed_data_provider);
6061
}
61-
blocks.push_back(current_block);
6262
}
6363
{
6464
(void)GetBlockProof(current_block);
@@ -68,9 +68,9 @@ FUZZ_TARGET_INIT(pow, initialize_pow)
6868
}
6969
}
7070
{
71-
const CBlockIndex* to = &PickValue(fuzzed_data_provider, blocks);
72-
const CBlockIndex* from = &PickValue(fuzzed_data_provider, blocks);
73-
const CBlockIndex* tip = &PickValue(fuzzed_data_provider, blocks);
71+
const auto& to = PickValue(fuzzed_data_provider, blocks);
72+
const auto& from = PickValue(fuzzed_data_provider, blocks);
73+
const auto& tip = PickValue(fuzzed_data_provider, blocks);
7474
try {
7575
(void)GetBlockProofEquivalentTime(*to, *from, *tip, consensus_params);
7676
} catch (const uint_error&) {

src/test/miner_tests.cpp

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

90-
static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
90+
static std::unique_ptr<CBlockIndex> CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
9191
{
92-
CBlockIndex index;
93-
index.nHeight = nHeight;
94-
index.pprev = active_chain_tip;
92+
auto index{std::make_unique<CBlockIndex>()};
93+
index->nHeight = nHeight;
94+
index->pprev = active_chain_tip;
9595
return index;
9696
}
9797

@@ -438,7 +438,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
438438

439439
{
440440
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
441-
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
441+
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, *CreateBlockIndex(active_chain_tip->nHeight + 2, active_chain_tip))); // Sequence locks pass on 2nd block
442442
}
443443

444444
// relative time locked
@@ -455,7 +455,7 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
455455
m_node.chainman->ActiveChain().Tip()->GetAncestor(m_node.chainman->ActiveChain().Tip()->nHeight - i)->nTime += SEQUENCE_LOCK_TIME; // Trick the MedianTimePast
456456
{
457457
CBlockIndex* active_chain_tip = m_node.chainman->ActiveChain().Tip();
458-
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip)));
458+
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, *CreateBlockIndex(active_chain_tip->nHeight + 1, active_chain_tip)));
459459
}
460460

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

0 commit comments

Comments
 (0)