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