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