poolresource.cpp raw

   1  // Copyright (c) 2022 The Limenka 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  
  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 = 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());
  80          InsecureRandomContext(entry.seed).fillrand(expect);
  81          assert(std::ranges::equal(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  
 113          PoolResourceTester::CheckAllDataAccountedFor(m_test_resource);
 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  }
 148