Branch data Line data Source code
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 : 721 : 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 : 499025 : 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 : 721 : PoolResourceFuzzer(FuzzedDataProvider& provider)
40 : 721 : : m_provider{provider},
41 : 721 : m_test_resource{provider.ConsumeIntegralInRange<size_t>(MAX_BLOCK_SIZE_BYTES, 262144)}
42 : : {
43 : 721 : }
44 : :
45 : 499025 : void Allocate(size_t size, size_t alignment)
46 : : {
47 [ - + ]: 499025 : assert(size > 0); // Must allocate at least 1 byte.
48 [ - + ]: 499025 : assert(alignment > 0); // Alignment must be at least 1.
49 [ - + ]: 499025 : assert((alignment & (alignment - 1)) == 0); // Alignment must be power of 2.
50 [ - + ]: 499025 : assert((size & (alignment - 1)) == 0); // Size must be a multiple of alignment.
51 : :
52 [ - + ]: 499025 : auto span = std::span(static_cast<std::byte*>(m_test_resource.Allocate(size, alignment)), size);
53 : 499025 : m_total_allocated += size;
54 : :
55 : 499025 : auto ptr_val = reinterpret_cast<std::uintptr_t>(span.data());
56 [ - + ]: 499025 : assert((ptr_val & (alignment - 1)) == 0);
57 : :
58 : 499025 : uint64_t seed = m_sequence++;
59 : 499025 : RandomContentFill(m_entries.emplace_back(span, alignment, seed));
60 : 499025 : }
61 : :
62 : : void
63 : 501871 : Allocate()
64 : : {
65 [ + + ]: 501871 : if (m_total_allocated > 16_MiB) return;
66 : 499025 : size_t alignment_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 7);
67 : 499025 : size_t alignment = size_t{1} << alignment_bits;
68 : 499025 : size_t size_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 16 - alignment_bits);
69 : 499025 : size_t size = m_provider.ConsumeIntegralInRange<size_t>(size_t{1} << size_bits, (size_t{1} << (size_bits + 1)) - 1U) << alignment_bits;
70 : 499025 : Allocate(size, alignment);
71 : : }
72 : :
73 : 499025 : void RandomContentFill(Entry& entry)
74 : : {
75 : 499025 : InsecureRandomContext(entry.seed).fillrand(entry.span);
76 : 499025 : }
77 : :
78 : 499025 : void RandomContentCheck(const Entry& entry)
79 : : {
80 : 499025 : std::vector<std::byte> expect(entry.span.size());
81 [ - + ]: 499025 : InsecureRandomContext(entry.seed).fillrand(expect);
82 [ - + ]: 499025 : assert(std::ranges::equal(entry.span, expect));
83 : 499025 : }
84 : :
85 : 499025 : void Deallocate(const Entry& entry)
86 : : {
87 [ - + ]: 499025 : auto ptr_val = reinterpret_cast<std::uintptr_t>(entry.span.data());
88 [ - + ]: 499025 : assert((ptr_val & (entry.alignment - 1)) == 0);
89 : 499025 : RandomContentCheck(entry);
90 : 499025 : m_total_allocated -= entry.span.size();
91 : 499025 : m_test_resource.Deallocate(entry.span.data(), entry.span.size(), entry.alignment);
92 : 499025 : }
93 : :
94 : 558388 : void Deallocate()
95 : : {
96 [ + + ]: 558388 : if (m_entries.empty()) {
97 : : return;
98 : : }
99 : :
100 [ - + ]: 499025 : size_t idx = m_provider.ConsumeIntegralInRange<size_t>(0, m_entries.size() - 1);
101 : 499025 : Deallocate(m_entries[idx]);
102 [ - + + + ]: 499025 : if (idx != m_entries.size() - 1) {
103 : 495243 : m_entries[idx] = std::move(m_entries.back());
104 : : }
105 : 499025 : m_entries.pop_back();
106 : : }
107 : :
108 : 721 : void Clear()
109 : : {
110 [ + + ]: 489697 : while (!m_entries.empty()) {
111 : 488976 : Deallocate();
112 : : }
113 : :
114 : 721 : PoolResourceTester::CheckAllDataAccountedFor(m_test_resource);
115 : 721 : }
116 : :
117 : 721 : void Fuzz()
118 : : {
119 [ + + + + ]: 572004 : LIMITED_WHILE(m_provider.ConsumeBool(), 10000)
120 : : {
121 : 571283 : CallOneOf(
122 : : m_provider,
123 : 501871 : [&] { Allocate(); },
124 : 69412 : [&] { Deallocate(); });
125 : : }
126 : 721 : Clear();
127 : 721 : }
128 : : };
129 : :
130 : :
131 : : } // namespace
132 : :
133 [ + - ]: 1179 : FUZZ_TARGET(pool_resource)
134 : : {
135 : 721 : FuzzedDataProvider provider(buffer.data(), buffer.size());
136 : 721 : CallOneOf(
137 : : provider,
138 [ + - ]: 174 : [&] { PoolResourceFuzzer<128, 1>{provider}.Fuzz(); },
139 [ + - ]: 170 : [&] { PoolResourceFuzzer<128, 2>{provider}.Fuzz(); },
140 [ + - ]: 186 : [&] { PoolResourceFuzzer<128, 4>{provider}.Fuzz(); },
141 [ + - ]: 190 : [&] { PoolResourceFuzzer<128, 8>{provider}.Fuzz(); },
142 : :
143 [ + - ]: 168 : [&] { PoolResourceFuzzer<8, 8>{provider}.Fuzz(); },
144 [ + - ]: 208 : [&] { PoolResourceFuzzer<16, 16>{provider}.Fuzz(); },
145 : :
146 [ + - ]: 164 : [&] { PoolResourceFuzzer<256, alignof(max_align_t)>{provider}.Fuzz(); },
147 [ + - ]: 182 : [&] { PoolResourceFuzzer<256, 64>{provider}.Fuzz(); });
148 : 721 : }
|