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