poolresource.cpp raw

   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  }
 149