/ src / test / fuzz / poolresource.cpp
poolresource.cpp
  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>
  6  #include <support/allocators/pool.h>
  7  #include <test/fuzz/FuzzedDataProvider.h>
  8  #include <test/fuzz/fuzz.h>
  9  #include <test/fuzz/util.h>
 10  #include <test/util/poolresourcetester.h>
 11  #include <test/util/xoroshiro128plusplus.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;
 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          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  
140          PoolResourceTester::CheckAllDataAccountedFor(m_test_resource);
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  }