Skip to content

Commit 551dc7f

Browse files
committed
Merge #18806: net: remove is{Empty,Full} flags from CBloomFilter, clarify CVE fix
1ad8ea2 net: remove is{Empty,Full} flags from CBloomFilter, clarify CVE fix (Sebastian Falbesoner) Pull request description: The BIP37 bloom filter class `CBloomFilter` contains two flags `isEmpty`/`isFull` together with an update method with the purpose to, according to the comments, "avoid wasting cpu", i.e. the mechanism should serve as an optimization for the trivial cases of empty (all bits zero) or full (all bits one) filters. However, the real reason of adding those flags (introduced with commit bitcoin/bitcoin@37c6389 by gmaxwell) was a _covert fix_ of [CVE-2013-5700](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-5700), a vulnerability that allowed a divide-by-zero remote node crash. According to gmaxwell himself (bitcoin/bitcoin#9060 (comment)): > the IsEmpty/IsFull optimizations were largely a pretextual optimization intended to make unexploitable a remote crash vulnerability (integer division by zero) that existed in the original bloom filtering code without disclosing it. I'm doubtful that they are all that useful. :) For more information on how to trigger this crash, see PR bitcoin/bitcoin#18515 which contains a detailled description and a regression test. It has also been discussed on a [recent PR club meeting on fuzzing](https://bitcoincore.reviews/18521.html). The covert fix code already led to issues and PR based on the wrong assumption that the flags are there for optimization reasons (see #16886 and #16922). This PR gets rid of the flags and the update method and just focuses on the CVE fix itself, i.e. it can be seen as a revert of the covert fix commit modulo the actual fix. ACKs for top commit: meshcollider: utACK 1ad8ea2 laanwj: Concept and code review ACK 1ad8ea2 jkczyz: ACK 1ad8ea2 MarcoFalke: ACK 1ad8ea2 fjahr: Code review ACK 1ad8ea2 Tree-SHA512: 29f7ff9faece0285e11e16c024851f5bcb772dec64118ccc3f9067ec256267ec8e1b1e3105c7de2a72fd122c3b085e8fc840ab8f4e49813f1cc7a444df1867f7
2 parents d96fdc2 + 1ad8ea2 commit 551dc7f

File tree

4 files changed

+5
-34
lines changed

4 files changed

+5
-34
lines changed

src/bloom.cpp

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, c
3131
* Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits
3232
* See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas
3333
*/
34-
isFull(false),
35-
isEmpty(true),
3634
nHashFuncs(std::min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)),
3735
nTweak(nTweakIn),
3836
nFlags(nFlagsIn)
@@ -47,15 +45,14 @@ inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector<
4745

4846
void CBloomFilter::insert(const std::vector<unsigned char>& vKey)
4947
{
50-
if (isFull)
48+
if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700)
5149
return;
5250
for (unsigned int i = 0; i < nHashFuncs; i++)
5351
{
5452
unsigned int nIndex = Hash(i, vKey);
5553
// Sets bit nIndex of vData
5654
vData[nIndex >> 3] |= (1 << (7 & nIndex));
5755
}
58-
isEmpty = false;
5956
}
6057

6158
void CBloomFilter::insert(const COutPoint& outpoint)
@@ -74,10 +71,8 @@ void CBloomFilter::insert(const uint256& hash)
7471

7572
bool CBloomFilter::contains(const std::vector<unsigned char>& vKey) const
7673
{
77-
if (isFull)
74+
if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700)
7875
return true;
79-
if (isEmpty)
80-
return false;
8176
for (unsigned int i = 0; i < nHashFuncs; i++)
8277
{
8378
unsigned int nIndex = Hash(i, vKey);
@@ -112,10 +107,8 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
112107
bool fFound = false;
113108
// Match if the filter contains the hash of tx
114109
// for finding tx when they appear in a block
115-
if (isFull)
110+
if (vData.empty()) // zero-size = "match-all" filter
116111
return true;
117-
if (isEmpty)
118-
return false;
119112
const uint256& hash = tx.GetHash();
120113
if (contains(hash))
121114
fFound = true;
@@ -177,19 +170,6 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
177170
return false;
178171
}
179172

180-
void CBloomFilter::UpdateEmptyFull()
181-
{
182-
bool full = true;
183-
bool empty = true;
184-
for (unsigned int i = 0; i < vData.size(); i++)
185-
{
186-
full &= vData[i] == 0xff;
187-
empty &= vData[i] == 0;
188-
}
189-
isFull = full;
190-
isEmpty = empty;
191-
}
192-
193173
CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const double fpRate)
194174
{
195175
double logFpRate = log(fpRate);

src/bloom.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ class CBloomFilter
4545
{
4646
private:
4747
std::vector<unsigned char> vData;
48-
bool isFull;
49-
bool isEmpty;
5048
unsigned int nHashFuncs;
5149
unsigned int nTweak;
5250
unsigned char nFlags;
@@ -64,7 +62,7 @@ class CBloomFilter
6462
* nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK)
6563
*/
6664
CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweak, unsigned char nFlagsIn);
67-
CBloomFilter() : isFull(true), isEmpty(false), nHashFuncs(0), nTweak(0), nFlags(0) {}
65+
CBloomFilter() : nHashFuncs(0), nTweak(0), nFlags(0) {}
6866

6967
ADD_SERIALIZE_METHODS;
7068

@@ -90,9 +88,6 @@ class CBloomFilter
9088

9189
//! Also adds any outputs which match the filter to the filter (to match their spending txes)
9290
bool IsRelevantAndUpdate(const CTransaction& tx);
93-
94-
//! Checks for empty and full filters to avoid wasting cpu
95-
void UpdateEmptyFull();
9691
};
9792

9893
/**

src/net_processing.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3216,7 +3216,6 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec
32163216
{
32173217
LOCK(pfrom->m_tx_relay->cs_filter);
32183218
pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter));
3219-
pfrom->m_tx_relay->pfilter->UpdateEmptyFull();
32203219
pfrom->m_tx_relay->fRelayTxes = true;
32213220
}
32223221
return true;

src/test/fuzz/bloom_filter.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
2525
fuzzed_data_provider.ConsumeIntegral<unsigned int>(),
2626
static_cast<unsigned char>(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))};
2727
while (fuzzed_data_provider.remaining_bytes() > 0) {
28-
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) {
28+
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 3)) {
2929
case 0: {
3030
const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
3131
(void)bloom_filter.contains(b);
@@ -65,9 +65,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
6565
(void)bloom_filter.IsRelevantAndUpdate(tx);
6666
break;
6767
}
68-
case 4:
69-
bloom_filter.UpdateEmptyFull();
70-
break;
7168
}
7269
(void)bloom_filter.IsWithinSizeConstraints();
7370
}

0 commit comments

Comments
 (0)