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