@@ -172,11 +172,18 @@ class TxOrphanageImpl final : public TxOrphanage {
172172 template <typename Tag>
173173 void Erase (Iter<Tag> it);
174174
175+ /* * Erase by wtxid. */
176+ bool EraseTxInternal (const Wtxid& wtxid);
177+
175178 /* * Check if there is exactly one announcement with the same wtxid as it. */
176179 bool IsUnique (Iter<ByWtxid> it) const ;
177180
178181 /* * Check if the orphanage needs trimming. */
179182 bool NeedsTrim () const ;
183+
184+ /* * Limit the orphanage to MaxGlobalLatencyScore and MaxGlobalUsage. */
185+ void LimitOrphans ();
186+
180187public:
181188 TxOrphanageImpl () = default ;
182189 TxOrphanageImpl (Count max_global_ann, Usage reserved_peer_usage) :
@@ -216,7 +223,6 @@ class TxOrphanageImpl final : public TxOrphanage {
216223 bool EraseTx (const Wtxid& wtxid) override ;
217224 void EraseForPeer (NodeId peer) override ;
218225 void EraseForBlock (const CBlock& block) override ;
219- void LimitOrphans () override ;
220226 std::vector<std::pair<Wtxid, NodeId>> AddChildrenToWorkSet (const CTransaction& tx, FastRandomContext& rng) override ;
221227 bool HaveTxToReconsider (NodeId peer) override ;
222228 std::vector<CTransactionRef> GetChildrenFromSamePeer (const CTransactionRef& parent, NodeId nodeid) const override ;
@@ -332,6 +338,9 @@ bool TxOrphanageImpl::AddTx(const CTransactionRef& tx, NodeId peer)
332338 peer, txid.ToString (), wtxid.ToString ());
333339 Assume (!IsUnique (iter));
334340 }
341+
342+ // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
343+ LimitOrphans ();
335344 return brand_new;
336345}
337346
@@ -360,10 +369,13 @@ bool TxOrphanageImpl::AddAnnouncer(const Wtxid& wtxid, NodeId peer)
360369 peer, txid.ToString (), wtxid.ToString ());
361370
362371 Assume (!IsUnique (iter));
372+
373+ // DoS prevention: do not allow m_orphanage to grow unbounded (see CVE-2012-3789)
374+ LimitOrphans ();
363375 return true ;
364376}
365377
366- bool TxOrphanageImpl::EraseTx (const Wtxid& wtxid)
378+ bool TxOrphanageImpl::EraseTxInternal (const Wtxid& wtxid)
367379{
368380 auto & index_by_wtxid = m_orphans.get <ByWtxid>();
369381
@@ -378,12 +390,21 @@ bool TxOrphanageImpl::EraseTx(const Wtxid& wtxid)
378390 Erase<ByWtxid>(it++);
379391 num_ann += 1 ;
380392 }
381-
382393 LogDebug (BCLog::TXPACKAGES, " removed orphan tx %s (wtxid=%s) (%u announcements)\n " , txid.ToString (), wtxid.ToString (), num_ann);
383394
384395 return true ;
385396}
386397
398+ bool TxOrphanageImpl::EraseTx (const Wtxid& wtxid)
399+ {
400+ const auto ret = EraseTxInternal (wtxid);
401+
402+ // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
403+ LimitOrphans ();
404+
405+ return ret;
406+ }
407+
387408/* * Erase all entries by this peer. */
388409void TxOrphanageImpl::EraseForPeer (NodeId peer)
389410{
@@ -400,6 +421,9 @@ void TxOrphanageImpl::EraseForPeer(NodeId peer)
400421 Assume (!m_peer_orphanage_info.contains (peer));
401422
402423 if (num_ann > 0 ) LogDebug (BCLog::TXPACKAGES, " Erased %d orphan transaction(s) from peer=%d\n " , num_ann, peer);
424+
425+ // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
426+ LimitOrphans ();
403427}
404428
405429/* * If the data structure needs trimming, evicts announcements by selecting the DoSiest peer and evicting its oldest
@@ -565,6 +589,7 @@ bool TxOrphanageImpl::HaveTxToReconsider(NodeId peer)
565589 auto it = m_orphans.get <ByPeer>().lower_bound (ByPeerView{peer, true , 0 });
566590 return it != m_orphans.get <ByPeer>().end () && it->m_announcer == peer && it->m_reconsider ;
567591}
592+
568593void TxOrphanageImpl::EraseForBlock (const CBlock& block)
569594{
570595 std::set<Wtxid> wtxids_to_erase;
@@ -583,13 +608,19 @@ void TxOrphanageImpl::EraseForBlock(const CBlock& block)
583608
584609 unsigned int num_erased{0 };
585610 for (const auto & wtxid : wtxids_to_erase) {
586- num_erased += EraseTx (wtxid) ? 1 : 0 ;
611+ // Don't use EraseTx here because it calls LimitOrphans and announcements deleted in that call are not reflected
612+ // in its return result. Waiting until the end to do LimitOrphans helps save repeated computation and allows us
613+ // to check that num_erased is what we expect.
614+ num_erased += EraseTxInternal (wtxid) ? 1 : 0 ;
587615 }
588616
589617 if (num_erased != 0 ) {
590618 LogDebug (BCLog::TXPACKAGES, " Erased %d orphan transaction(s) included or conflicted by block\n " , num_erased);
591619 }
592620 Assume (wtxids_to_erase.size () == num_erased);
621+
622+ // Deletions can cause the orphanage's MaxGlobalUsage to decrease, so we may need to trim here.
623+ LimitOrphans ();
593624}
594625
595626/* * Get all children that spend from this tx and were received from nodeid. Sorted from most
@@ -697,6 +728,8 @@ void TxOrphanageImpl::SanityCheck() const
697728 const auto summed_peer_latency_score = std::accumulate (m_peer_orphanage_info.begin (), m_peer_orphanage_info.end (),
698729 TxOrphanage::Count{0 }, [](TxOrphanage::Count sum, const auto pair) { return sum + pair.second .m_total_latency_score ; });
699730 assert (summed_peer_latency_score >= m_unique_rounded_input_scores + m_orphans.size ());
731+
732+ assert (!NeedsTrim ());
700733}
701734
702735TxOrphanage::Count TxOrphanageImpl::MaxGlobalLatencyScore () const { return m_max_global_latency_score; }
0 commit comments