Bitcoin Core  27.99.0
P2P Digital Currency
poolresource.cpp
Go to the documentation of this file.
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 <span.h>
8 #include <test/fuzz/fuzz.h>
9 #include <test/fuzz/util.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 class PoolResourceFuzzer
21 {
22  FuzzedDataProvider& m_provider;
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  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  PoolResourceFuzzer(FuzzedDataProvider& provider)
39  : m_provider{provider},
40  m_test_resource{provider.ConsumeIntegralInRange<size_t>(MAX_BLOCK_SIZE_BYTES, 262144)}
41  {
42  }
43 
44  void Allocate(size_t size, size_t alignment)
45  {
46  assert(size > 0); // Must allocate at least 1 byte.
47  assert(alignment > 0); // Alignment must be at least 1.
48  assert((alignment & (alignment - 1)) == 0); // Alignment must be power of 2.
49  assert((size & (alignment - 1)) == 0); // Size must be a multiple of alignment.
50 
51  auto span = Span(static_cast<std::byte*>(m_test_resource.Allocate(size, alignment)), size);
52  m_total_allocated += size;
53 
54  auto ptr_val = reinterpret_cast<std::uintptr_t>(span.data());
55  assert((ptr_val & (alignment - 1)) == 0);
56 
57  uint64_t seed = m_sequence++;
58  RandomContentFill(m_entries.emplace_back(span, alignment, seed));
59  }
60 
61  void
62  Allocate()
63  {
64  if (m_total_allocated > 0x1000000) return;
65  size_t alignment_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 7);
66  size_t alignment = 1 << alignment_bits;
67  size_t size_bits = m_provider.ConsumeIntegralInRange<size_t>(0, 16 - alignment_bits);
68  size_t size = m_provider.ConsumeIntegralInRange<size_t>(1U << size_bits, (1U << (size_bits + 1)) - 1U) << alignment_bits;
69  Allocate(size, alignment);
70  }
71 
72  void RandomContentFill(Entry& entry)
73  {
74  XoRoShiRo128PlusPlus rng(entry.seed);
75  auto ptr = entry.span.data();
76  auto size = entry.span.size();
77 
78  while (size >= 8) {
79  auto r = rng();
80  std::memcpy(ptr, &r, 8);
81  size -= 8;
82  ptr += 8;
83  }
84  if (size > 0) {
85  auto r = rng();
86  std::memcpy(ptr, &r, size);
87  }
88  }
89 
90  void RandomContentCheck(const Entry& entry)
91  {
92  XoRoShiRo128PlusPlus rng(entry.seed);
93  auto ptr = entry.span.data();
94  auto size = entry.span.size();
95 
96  std::byte buf[8];
97  while (size >= 8) {
98  auto r = rng();
99  std::memcpy(buf, &r, 8);
100  assert(std::memcmp(buf, ptr, 8) == 0);
101  size -= 8;
102  ptr += 8;
103  }
104  if (size > 0) {
105  auto r = rng();
106  std::memcpy(buf, &r, size);
107  assert(std::memcmp(buf, ptr, size) == 0);
108  }
109  }
110 
111  void Deallocate(const Entry& entry)
112  {
113  auto ptr_val = reinterpret_cast<std::uintptr_t>(entry.span.data());
114  assert((ptr_val & (entry.alignment - 1)) == 0);
115  RandomContentCheck(entry);
116  m_total_allocated -= entry.span.size();
117  m_test_resource.Deallocate(entry.span.data(), entry.span.size(), entry.alignment);
118  }
119 
120  void Deallocate()
121  {
122  if (m_entries.empty()) {
123  return;
124  }
125 
126  size_t idx = m_provider.ConsumeIntegralInRange<size_t>(0, m_entries.size() - 1);
127  Deallocate(m_entries[idx]);
128  if (idx != m_entries.size() - 1) {
129  m_entries[idx] = std::move(m_entries.back());
130  }
131  m_entries.pop_back();
132  }
133 
134  void Clear()
135  {
136  while (!m_entries.empty()) {
137  Deallocate();
138  }
139 
141  }
142 
143  void Fuzz()
144  {
145  LIMITED_WHILE(m_provider.ConsumeBool(), 10000)
146  {
147  CallOneOf(
148  m_provider,
149  [&] { Allocate(); },
150  [&] { Deallocate(); });
151  }
152  Clear();
153  }
154 };
155 
156 
157 } // namespace
158 
159 FUZZ_TARGET(pool_resource)
160 {
161  FuzzedDataProvider provider(buffer.data(), buffer.size());
162  CallOneOf(
163  provider,
164  [&] { PoolResourceFuzzer<128, 1>{provider}.Fuzz(); },
165  [&] { PoolResourceFuzzer<128, 2>{provider}.Fuzz(); },
166  [&] { PoolResourceFuzzer<128, 4>{provider}.Fuzz(); },
167  [&] { PoolResourceFuzzer<128, 8>{provider}.Fuzz(); },
168 
169  [&] { PoolResourceFuzzer<8, 8>{provider}.Fuzz(); },
170  [&] { PoolResourceFuzzer<16, 16>{provider}.Fuzz(); },
171 
172  [&] { PoolResourceFuzzer<256, alignof(max_align_t)>{provider}.Fuzz(); },
173  [&] { PoolResourceFuzzer<256, 64>{provider}.Fuzz(); });
174 }
A memory resource similar to std::pmr::unsynchronized_pool_resource, but optimized for node-based con...
Definition: pool.h:71
void Deallocate(void *p, std::size_t bytes, std::size_t alignment) noexcept
Returns a block to the freelists, or deletes the block when it did not come from the chunks.
Definition: pool.h:241
void * Allocate(std::size_t bytes, std::size_t alignment)
Allocates a block of bytes.
Definition: pool.h:212
static void CheckAllDataAccountedFor(const PoolResource< MAX_BLOCK_SIZE_BYTES, ALIGN_BYTES > &resource)
Once all blocks are given back to the resource, tests that the freelists are consistent:
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:98
xoroshiro128++ PRNG.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:23
FUZZ_TARGET(pool_resource)
Span(T *, EndOrSize) -> Span< T >
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
assert(!tx.IsCoinBase())