Branch data 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 : 0 : 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 : 0 : size_t m_total_allocated{};
26 : :
27 : : struct Entry {
28 : : Span<std::byte> span;
29 : : size_t alignment;
30 : 2 : 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 [ + - ][ + - ]: 6 : 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 : }
|