Skip to content

Commit c4c3f11

Browse files
author
MarcoFalke
committed
Merge #18190: tests: Add fuzzing harness for Golomb-Rice coding (GolombRiceEncode/GolombRiceDecode)
69749fb tests: Add fuzzing harness for Golomb-Rice coding (GolombRiceEncode/GolombRiceDecode) (practicalswift) Pull request description: Add fuzzing harness for Golomb-Rice coding (`GolombRiceEncode`/`GolombRiceDecode`). Test this PR using: ``` $ make distclean $ ./autogen.sh $ CC=clang CXX=clang++ ./configure --enable-fuzz \ --with-sanitizers=address,fuzzer,undefined $ make $ src/test/fuzz/golomb_rice … ``` Top commit has no ACKs. Tree-SHA512: 1b26512301b8c22ab3b804d9b9e4baf933f26f8c05e462d583863badcec7e694548a34849a0d7c4ff7d58b19f6338b51819976ecf642bc4659b04ef71182d748
2 parents 56d2ff8 + 69749fb commit c4c3f11

File tree

5 files changed

+164
-31
lines changed

5 files changed

+164
-31
lines changed

src/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ BITCOIN_CORE_H = \
215215
util/check.h \
216216
util/error.h \
217217
util/fees.h \
218+
util/golombrice.h \
218219
util/spanparsing.h \
219220
util/system.h \
220221
util/macros.h \

src/Makefile.test.include

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ FUZZ_TARGETS = \
4141
test/fuzz/flat_file_pos_deserialize \
4242
test/fuzz/flatfile \
4343
test/fuzz/float \
44+
test/fuzz/golomb_rice \
4445
test/fuzz/hex \
4546
test/fuzz/http_request \
4647
test/fuzz/integer \
@@ -517,6 +518,12 @@ test_fuzz_float_LDADD = $(FUZZ_SUITE_LD_COMMON)
517518
test_fuzz_float_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
518519
test_fuzz_float_SOURCES = test/fuzz/float.cpp
519520

521+
test_fuzz_golomb_rice_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
522+
test_fuzz_golomb_rice_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
523+
test_fuzz_golomb_rice_LDADD = $(FUZZ_SUITE_LD_COMMON)
524+
test_fuzz_golomb_rice_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
525+
test_fuzz_golomb_rice_SOURCES = test/fuzz/golomb_rice.cpp
526+
520527
test_fuzz_hex_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
521528
test_fuzz_hex_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
522529
test_fuzz_hex_LDADD = $(FUZZ_SUITE_LD_COMMON)

src/blockfilter.cpp

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <primitives/transaction.h>
1313
#include <script/script.h>
1414
#include <streams.h>
15+
#include <util/golombrice.h>
1516

1617
/// SerType used to serialize parameters in GCS filter encoding.
1718
static constexpr int GCS_SER_TYPE = SER_NETWORK;
@@ -23,37 +24,6 @@ static const std::map<BlockFilterType, std::string> g_filter_types = {
2324
{BlockFilterType::BASIC, "basic"},
2425
};
2526

26-
template <typename OStream>
27-
static void GolombRiceEncode(BitStreamWriter<OStream>& bitwriter, uint8_t P, uint64_t x)
28-
{
29-
// Write quotient as unary-encoded: q 1's followed by one 0.
30-
uint64_t q = x >> P;
31-
while (q > 0) {
32-
int nbits = q <= 64 ? static_cast<int>(q) : 64;
33-
bitwriter.Write(~0ULL, nbits);
34-
q -= nbits;
35-
}
36-
bitwriter.Write(0, 1);
37-
38-
// Write the remainder in P bits. Since the remainder is just the bottom
39-
// P bits of x, there is no need to mask first.
40-
bitwriter.Write(x, P);
41-
}
42-
43-
template <typename IStream>
44-
static uint64_t GolombRiceDecode(BitStreamReader<IStream>& bitreader, uint8_t P)
45-
{
46-
// Read unary-encoded quotient: q 1's followed by one 0.
47-
uint64_t q = 0;
48-
while (bitreader.Read(1) == 1) {
49-
++q;
50-
}
51-
52-
uint64_t r = bitreader.Read(P);
53-
54-
return (q << P) + r;
55-
}
56-
5727
// Map a value x that is uniformly distributed in the range [0, 2^64) to a
5828
// value uniformly distributed in [0, n) by returning the upper 64 bits of
5929
// x * n.

src/test/fuzz/golomb_rice.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright (c) 2020 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <blockfilter.h>
6+
#include <serialize.h>
7+
#include <streams.h>
8+
#include <test/fuzz/fuzz.h>
9+
#include <test/fuzz/FuzzedDataProvider.h>
10+
#include <test/fuzz/util.h>
11+
#include <util/bytevectorhash.h>
12+
#include <util/golombrice.h>
13+
14+
#include <algorithm>
15+
#include <cassert>
16+
#include <cstdint>
17+
#include <iosfwd>
18+
#include <unordered_set>
19+
#include <vector>
20+
21+
namespace {
22+
uint64_t MapIntoRange(const uint64_t x, const uint64_t n)
23+
{
24+
const uint64_t x_hi = x >> 32;
25+
const uint64_t x_lo = x & 0xFFFFFFFF;
26+
const uint64_t n_hi = n >> 32;
27+
const uint64_t n_lo = n & 0xFFFFFFFF;
28+
const uint64_t ac = x_hi * n_hi;
29+
const uint64_t ad = x_hi * n_lo;
30+
const uint64_t bc = x_lo * n_hi;
31+
const uint64_t bd = x_lo * n_lo;
32+
const uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF);
33+
const uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32);
34+
return upper64;
35+
}
36+
37+
uint64_t HashToRange(const std::vector<uint8_t>& element, const uint64_t f)
38+
{
39+
const uint64_t hash = CSipHasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL)
40+
.Write(element.data(), element.size())
41+
.Finalize();
42+
return MapIntoRange(hash, f);
43+
}
44+
45+
std::vector<uint64_t> BuildHashedSet(const std::unordered_set<std::vector<uint8_t>, ByteVectorHash>& elements, const uint64_t f)
46+
{
47+
std::vector<uint64_t> hashed_elements;
48+
hashed_elements.reserve(elements.size());
49+
for (const std::vector<uint8_t>& element : elements) {
50+
hashed_elements.push_back(HashToRange(element, f));
51+
}
52+
std::sort(hashed_elements.begin(), hashed_elements.end());
53+
return hashed_elements;
54+
}
55+
} // namespace
56+
57+
void test_one_input(const std::vector<uint8_t>& buffer)
58+
{
59+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
60+
std::vector<uint8_t> golomb_rice_data;
61+
std::vector<uint64_t> encoded_deltas;
62+
{
63+
std::unordered_set<std::vector<uint8_t>, ByteVectorHash> elements;
64+
const int n = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 512);
65+
for (int i = 0; i < n; ++i) {
66+
elements.insert(ConsumeRandomLengthByteVector(fuzzed_data_provider, 16));
67+
}
68+
CVectorWriter stream(SER_NETWORK, 0, golomb_rice_data, 0);
69+
WriteCompactSize(stream, static_cast<uint32_t>(elements.size()));
70+
BitStreamWriter<CVectorWriter> bitwriter(stream);
71+
if (!elements.empty()) {
72+
uint64_t last_value = 0;
73+
for (const uint64_t value : BuildHashedSet(elements, static_cast<uint64_t>(elements.size()) * static_cast<uint64_t>(BASIC_FILTER_M))) {
74+
const uint64_t delta = value - last_value;
75+
encoded_deltas.push_back(delta);
76+
GolombRiceEncode(bitwriter, BASIC_FILTER_P, delta);
77+
last_value = value;
78+
}
79+
}
80+
bitwriter.Flush();
81+
}
82+
83+
std::vector<uint64_t> decoded_deltas;
84+
{
85+
VectorReader stream{SER_NETWORK, 0, golomb_rice_data, 0};
86+
BitStreamReader<VectorReader> bitreader(stream);
87+
const uint32_t n = static_cast<uint32_t>(ReadCompactSize(stream));
88+
for (uint32_t i = 0; i < n; ++i) {
89+
decoded_deltas.push_back(GolombRiceDecode(bitreader, BASIC_FILTER_P));
90+
}
91+
}
92+
93+
assert(encoded_deltas == decoded_deltas);
94+
95+
{
96+
const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider, 1024);
97+
VectorReader stream{SER_NETWORK, 0, random_bytes, 0};
98+
uint32_t n;
99+
try {
100+
n = static_cast<uint32_t>(ReadCompactSize(stream));
101+
} catch (const std::ios_base::failure&) {
102+
return;
103+
}
104+
BitStreamReader<VectorReader> bitreader(stream);
105+
for (uint32_t i = 0; i < std::min<uint32_t>(n, 1024); ++i) {
106+
try {
107+
(void)GolombRiceDecode(bitreader, BASIC_FILTER_P);
108+
} catch (const std::ios_base::failure&) {
109+
}
110+
}
111+
}
112+
}

src/util/golombrice.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2018-2019 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_UTIL_GOLOMBRICE_H
6+
#define BITCOIN_UTIL_GOLOMBRICE_H
7+
8+
#include <streams.h>
9+
10+
#include <cstdint>
11+
12+
template <typename OStream>
13+
void GolombRiceEncode(BitStreamWriter<OStream>& bitwriter, uint8_t P, uint64_t x)
14+
{
15+
// Write quotient as unary-encoded: q 1's followed by one 0.
16+
uint64_t q = x >> P;
17+
while (q > 0) {
18+
int nbits = q <= 64 ? static_cast<int>(q) : 64;
19+
bitwriter.Write(~0ULL, nbits);
20+
q -= nbits;
21+
}
22+
bitwriter.Write(0, 1);
23+
24+
// Write the remainder in P bits. Since the remainder is just the bottom
25+
// P bits of x, there is no need to mask first.
26+
bitwriter.Write(x, P);
27+
}
28+
29+
template <typename IStream>
30+
uint64_t GolombRiceDecode(BitStreamReader<IStream>& bitreader, uint8_t P)
31+
{
32+
// Read unary-encoded quotient: q 1's followed by one 0.
33+
uint64_t q = 0;
34+
while (bitreader.Read(1) == 1) {
35+
++q;
36+
}
37+
38+
uint64_t r = bitreader.Read(P);
39+
40+
return (q << P) + r;
41+
}
42+
43+
#endif // BITCOIN_UTIL_GOLOMBRICE_H

0 commit comments

Comments
 (0)