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 <random.h>
6 #include <span.h>
9 #include <test/fuzz/fuzz.h>
10 #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 = size_t{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>(size_t{1} << size_bits, (size_t{1} << (size_bits + 1)) - 1U) << alignment_bits;
69  Allocate(size, alignment);
70  }
71 
72  void RandomContentFill(Entry& entry)
73  {
74  InsecureRandomContext(entry.seed).fillrand(entry.span);
75  }
76 
77  void RandomContentCheck(const Entry& entry)
78  {
79  std::vector<std::byte> expect(entry.span.size());
81  assert(entry.span == expect);
82  }
83 
84  void Deallocate(const Entry& entry)
85  {
86  auto ptr_val = reinterpret_cast<std::uintptr_t>(entry.span.data());
87  assert((ptr_val & (entry.alignment - 1)) == 0);
88  RandomContentCheck(entry);
89  m_total_allocated -= entry.span.size();
90  m_test_resource.Deallocate(entry.span.data(), entry.span.size(), entry.alignment);
91  }
92 
93  void Deallocate()
94  {
95  if (m_entries.empty()) {
96  return;
97  }
98 
99  size_t idx = m_provider.ConsumeIntegralInRange<size_t>(0, m_entries.size() - 1);
100  Deallocate(m_entries[idx]);
101  if (idx != m_entries.size() - 1) {
102  m_entries[idx] = std::move(m_entries.back());
103  }
104  m_entries.pop_back();
105  }
106 
107  void Clear()
108  {
109  while (!m_entries.empty()) {
110  Deallocate();
111  }
112 
114  }
115 
116  void Fuzz()
117  {
118  LIMITED_WHILE(m_provider.ConsumeBool(), 10000)
119  {
120  CallOneOf(
121  m_provider,
122  [&] { Allocate(); },
123  [&] { Deallocate(); });
124  }
125  Clear();
126  }
127 };
128 
129 
130 } // namespace
131 
132 FUZZ_TARGET(pool_resource)
133 {
134  FuzzedDataProvider provider(buffer.data(), buffer.size());
135  CallOneOf(
136  provider,
137  [&] { PoolResourceFuzzer<128, 1>{provider}.Fuzz(); },
138  [&] { PoolResourceFuzzer<128, 2>{provider}.Fuzz(); },
139  [&] { PoolResourceFuzzer<128, 4>{provider}.Fuzz(); },
140  [&] { PoolResourceFuzzer<128, 8>{provider}.Fuzz(); },
141 
142  [&] { PoolResourceFuzzer<8, 8>{provider}.Fuzz(); },
143  [&] { PoolResourceFuzzer<16, 16>{provider}.Fuzz(); },
144 
145  [&] { PoolResourceFuzzer<256, alignof(max_align_t)>{provider}.Fuzz(); },
146  [&] { PoolResourceFuzzer<256, 64>{provider}.Fuzz(); });
147 }
xoroshiro128++ PRNG.
Definition: random.h:416
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:
void fillrand(Span< std::byte > span) noexcept
Fill a Span with random bytes.
Definition: random.h:267
A Span is an object that can refer to a contiguous sequence of objects.
Definition: span.h:98
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
FUZZ_TARGET(pool_resource)
Span(T *, EndOrSize) -> Span< T >
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
#define expect(bit)
assert(!tx.IsCoinBase())