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 }