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