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