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