Skip to content

Commit 1afca6b

Browse files
martinussipa
andcommitted
Add PoolResource fuzzer
Fuzzes PoolResource with random allocations/deallocations, and multiple asserts. Co-Authored-By: Pieter Wuille <pieter@wuille.net>
1 parent e19943f commit 1afca6b

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ test_fuzz_fuzz_SOURCES = \
301301
test/fuzz/partially_downloaded_block.cpp \
302302
test/fuzz/policy_estimator.cpp \
303303
test/fuzz/policy_estimator_io.cpp \
304+
test/fuzz/poolresource.cpp \
304305
test/fuzz/pow.cpp \
305306
test/fuzz/prevector.cpp \
306307
test/fuzz/primitives_transaction.cpp \

src/test/fuzz/poolresource.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// Copyright (c) 2022 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 <span.h>
6+
#include <support/allocators/pool.h>
7+
#include <test/fuzz/FuzzedDataProvider.h>
8+
#include <test/fuzz/fuzz.h>
9+
#include <test/fuzz/util.h>
10+
#include <test/util/poolresourcetester.h>
11+
#include <test/util/xoroshiro128plusplus.h>
12+
13+
#include <cstdint>
14+
#include <tuple>
15+
#include <vector>
16+
17+
namespace {
18+
19+
template <std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
20+
class PoolResourceFuzzer
21+
{
22+
FuzzedDataProvider& m_provider;
23+
PoolResource<MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES> m_test_resource;
24+
uint64_t m_sequence{0};
25+
size_t m_total_allocated{};
26+
27+
struct Entry {
28+
Span<std::byte> span;
29+
size_t alignment;
30+
uint64_t seed;
31+
32+
Entry(Span<std::byte> s, size_t a, uint64_t se) : span(s), alignment(a), seed(se) {}
33+
};
34+
35+
std::vector<Entry> m_entries;
36+
37+
public:
38+
PoolResourceFuzzer(FuzzedDataProvider& provider)
39+
: m_provider{provider},
40+
m_test_resource{provider.ConsumeIntegralInRange<size_t>(MAX_BLOCK_SIZE_BYTES, 262144)}
41+
{
42+
}
43+
44+
void Allocate(size_t size, size_t alignment)
45+
{
46+
assert(size > 0); // Must allocate at least 1 byte.
47+
assert(alignment > 0); // Alignment must be at least 1.
48+
assert((alignment & (alignment - 1)) == 0); // Alignment must be power of 2.
49+
assert((size & (alignment - 1)) == 0); // Size must be a multiple of alignment.
50+
51+
auto span = Span(static_cast<std::byte*>(m_test_resource.Allocate(size, alignment)), size);
52+
m_total_allocated += size;
53+
54+
auto ptr_val = reinterpret_cast<std::uintptr_t>(span.data());
55+
assert((ptr_val & (alignment - 1)) == 0);
56+
57+
uint64_t seed = m_sequence++;
58+
RandomContentFill(m_entries.emplace_back(span, alignment, seed));
59+
}
60+
61+
void
62+
Allocate()
63+
{
64+
if (m_total_allocated > 0x1000000) return;
65+
size_t alignment_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 7);
66+
size_t alignment = 1 << alignment_bits;
67+
size_t size_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 16 - alignment_bits);
68+
size_t size = m_provider.ConsumeIntegralInRange<size_t>(1U << size_bits, (1U << (size_bits + 1)) - 1U) << alignment_bits;
69+
Allocate(size, alignment);
70+
}
71+
72+
void RandomContentFill(Entry& entry)
73+
{
74+
XoRoShiRo128PlusPlus rng(entry.seed);
75+
auto ptr = entry.span.data();
76+
auto size = entry.span.size();
77+
78+
while (size >= 8) {
79+
auto r = rng();
80+
std::memcpy(ptr, &r, 8);
81+
size -= 8;
82+
ptr += 8;
83+
}
84+
if (size > 0) {
85+
auto r = rng();
86+
std::memcpy(ptr, &r, size);
87+
}
88+
}
89+
90+
void RandomContentCheck(const Entry& entry)
91+
{
92+
XoRoShiRo128PlusPlus rng(entry.seed);
93+
auto ptr = entry.span.data();
94+
auto size = entry.span.size();
95+
96+
std::byte buf[8];
97+
while (size >= 8) {
98+
auto r = rng();
99+
std::memcpy(buf, &r, 8);
100+
assert(std::memcmp(buf, ptr, 8) == 0);
101+
size -= 8;
102+
ptr += 8;
103+
}
104+
if (size > 0) {
105+
auto r = rng();
106+
std::memcpy(buf, &r, size);
107+
assert(std::memcmp(buf, ptr, size) == 0);
108+
}
109+
}
110+
111+
void Deallocate(const Entry& entry)
112+
{
113+
auto ptr_val = reinterpret_cast<std::uintptr_t>(entry.span.data());
114+
assert((ptr_val & (entry.alignment - 1)) == 0);
115+
RandomContentCheck(entry);
116+
m_total_allocated -= entry.span.size();
117+
m_test_resource.Deallocate(entry.span.data(), entry.span.size(), entry.alignment);
118+
}
119+
120+
void Deallocate()
121+
{
122+
if (m_entries.empty()) {
123+
return;
124+
}
125+
126+
size_t idx = m_provider.ConsumeIntegralInRange<size_t>(0, m_entries.size() - 1);
127+
Deallocate(m_entries[idx]);
128+
if (idx != m_entries.size() - 1) {
129+
m_entries[idx] = std::move(m_entries.back());
130+
}
131+
m_entries.pop_back();
132+
}
133+
134+
void Clear()
135+
{
136+
while (!m_entries.empty()) {
137+
Deallocate();
138+
}
139+
140+
PoolResourceTester::CheckAllDataAccountedFor(m_test_resource);
141+
}
142+
143+
void Fuzz()
144+
{
145+
LIMITED_WHILE(m_provider.ConsumeBool(), 10000)
146+
{
147+
CallOneOf(
148+
m_provider,
149+
[&] { Allocate(); },
150+
[&] { Deallocate(); });
151+
}
152+
Clear();
153+
}
154+
};
155+
156+
157+
} // namespace
158+
159+
FUZZ_TARGET(pool_resource)
160+
{
161+
FuzzedDataProvider provider(buffer.data(), buffer.size());
162+
CallOneOf(
163+
provider,
164+
[&] { PoolResourceFuzzer<128, 1>{provider}.Fuzz(); },
165+
[&] { PoolResourceFuzzer<128, 2>{provider}.Fuzz(); },
166+
[&] { PoolResourceFuzzer<128, 4>{provider}.Fuzz(); },
167+
[&] { PoolResourceFuzzer<128, 8>{provider}.Fuzz(); },
168+
169+
[&] { PoolResourceFuzzer<8, 8>{provider}.Fuzz(); },
170+
[&] { PoolResourceFuzzer<16, 16>{provider}.Fuzz(); },
171+
172+
[&] { PoolResourceFuzzer<256, alignof(max_align_t)>{provider}.Fuzz(); },
173+
[&] { PoolResourceFuzzer<256, 64>{provider}.Fuzz(); });
174+
}

0 commit comments

Comments
 (0)