mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-27 14:25:02 +02:00
kernel: guard btck::Handle move-assignment against self-move
The move-assignment operator for btck::Handle<> unconditionally called DestroyFunc(m_ptr) before reading the source pointer. On a self-move (h = std::move(h)), this destroyed the held resource and then reassigned the now-dangling pointer back to m_ptr via std::exchange, leading to a double-free when the object is later destroyed. Mirror the existing self-check in the copy-assignment operator by guarding the move-assignment with 'if (this != &other)' so a self-move becomes a no-op, leaving the object in a valid state as required by the standard library. Handle<> is the base of 16 public types in the kernel C++ API wrapper (Transaction, Block, BlockHeader, ChainParams, Context, Coin, BlockValidationState, ScriptPubkey, TransactionOutput, Txid, OutPoint, TransactionInput, PrecomputedTransactionData, BlockHash, BlockSpentOutputs, TransactionSpentOutputs), so self-move can arise from generic algorithms operating on containers of these types. Extend CheckHandle in test_kernel to cover self-move-assignment for every Handle-derived type.
This commit is contained in:
@@ -341,8 +341,10 @@ public:
|
||||
Handle(Handle&& other) noexcept : m_ptr(other.m_ptr) { other.m_ptr = nullptr; }
|
||||
Handle& operator=(Handle&& other) noexcept
|
||||
{
|
||||
DestroyFunc(m_ptr);
|
||||
m_ptr = std::exchange(other.m_ptr, nullptr);
|
||||
if (this != &other) {
|
||||
DestroyFunc(m_ptr);
|
||||
m_ptr = std::exchange(other.m_ptr, nullptr);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -315,6 +315,16 @@ void CheckHandle(T object, T distinct_object)
|
||||
if constexpr (HasToBytes<T>) {
|
||||
check_equal(object2.ToBytes(), object3.ToBytes());
|
||||
}
|
||||
|
||||
// Self move-assignment must not destroy the held resource.
|
||||
// Use a reference to avoid -Wself-move warnings.
|
||||
original_ptr = object2.get();
|
||||
auto& object2_ref = object2;
|
||||
object2 = std::move(object2_ref);
|
||||
BOOST_CHECK_EQUAL(object2.get(), original_ptr);
|
||||
if constexpr (HasToBytes<T>) {
|
||||
check_equal(object2.ToBytes(), object3.ToBytes());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename RangeType>
|
||||
|
||||
Reference in New Issue
Block a user