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 }