Line data Source code
1 : // Copyright (c) 2012-2021 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 <support/allocators/mt_pooled_secure.h> 6 : #include <support/lockedpool.h> 7 : 8 : #include <limits> 9 : #include <memory> 10 : #include <stdexcept> 11 : #include <utility> 12 : #include <vector> 13 : 14 : #include <boost/test/unit_test.hpp> 15 : 16 146 : BOOST_AUTO_TEST_SUITE(allocator_tests) 17 : 18 148 : BOOST_AUTO_TEST_CASE(arena_tests) 19 : { 20 : // Fake memory base address for testing 21 : // without actually using memory. 22 1 : void *synth_base = reinterpret_cast<void*>(0x08000000); 23 1 : const size_t synth_size = 1024*1024; 24 1 : Arena b(synth_base, synth_size, 16); 25 1 : void *chunk = b.alloc(1000); 26 : #ifdef ARENA_DEBUG 27 : b.walk(); 28 : #endif 29 1 : BOOST_CHECK(chunk != nullptr); 30 1 : BOOST_CHECK(b.stats().used == 1008); // Aligned to 16 31 1 : BOOST_CHECK(b.stats().total == synth_size); // Nothing has disappeared? 32 1 : b.free(chunk); 33 : #ifdef ARENA_DEBUG 34 : b.walk(); 35 : #endif 36 1 : BOOST_CHECK(b.stats().used == 0); 37 1 : BOOST_CHECK(b.stats().free == synth_size); 38 : try { // Test exception on double-free 39 1 : b.free(chunk); 40 0 : BOOST_CHECK(0); 41 1 : } catch(std::runtime_error &) 42 : { 43 1 : } 44 : 45 1 : void *a0 = b.alloc(128); 46 1 : void *a1 = b.alloc(256); 47 1 : void *a2 = b.alloc(512); 48 1 : BOOST_CHECK(b.stats().used == 896); 49 1 : BOOST_CHECK(b.stats().total == synth_size); 50 : #ifdef ARENA_DEBUG 51 : b.walk(); 52 : #endif 53 1 : b.free(a0); 54 : #ifdef ARENA_DEBUG 55 : b.walk(); 56 : #endif 57 1 : BOOST_CHECK(b.stats().used == 768); 58 1 : b.free(a1); 59 1 : BOOST_CHECK(b.stats().used == 512); 60 1 : void *a3 = b.alloc(128); 61 : #ifdef ARENA_DEBUG 62 : b.walk(); 63 : #endif 64 1 : BOOST_CHECK(b.stats().used == 640); 65 1 : b.free(a2); 66 1 : BOOST_CHECK(b.stats().used == 128); 67 1 : b.free(a3); 68 1 : BOOST_CHECK(b.stats().used == 0); 69 1 : BOOST_CHECK_EQUAL(b.stats().chunks_used, 0U); 70 1 : BOOST_CHECK(b.stats().total == synth_size); 71 1 : BOOST_CHECK(b.stats().free == synth_size); 72 1 : BOOST_CHECK_EQUAL(b.stats().chunks_free, 1U); 73 : 74 1 : std::vector<void*> addr; 75 1 : BOOST_CHECK(b.alloc(0) == nullptr); // allocating 0 always returns nullptr 76 : #ifdef ARENA_DEBUG 77 : b.walk(); 78 : #endif 79 : // Sweeping allocate all memory 80 1025 : for (int x=0; x<1024; ++x) 81 1024 : addr.push_back(b.alloc(1024)); 82 1 : BOOST_CHECK(b.stats().free == 0); 83 1 : BOOST_CHECK(b.alloc(1024) == nullptr); // memory is full, this must return nullptr 84 1 : BOOST_CHECK(b.alloc(0) == nullptr); 85 1025 : for (int x=0; x<1024; ++x) 86 1024 : b.free(addr[x]); 87 1 : addr.clear(); 88 1 : BOOST_CHECK(b.stats().total == synth_size); 89 1 : BOOST_CHECK(b.stats().free == synth_size); 90 : 91 : // Now in the other direction... 92 1025 : for (int x=0; x<1024; ++x) 93 1024 : addr.push_back(b.alloc(1024)); 94 1025 : for (int x=0; x<1024; ++x) 95 1024 : b.free(addr[1023-x]); 96 1 : addr.clear(); 97 : 98 : // Now allocate in smaller unequal chunks, then deallocate haphazardly 99 : // Not all the chunks will succeed allocating, but freeing nullptr is 100 : // allowed so that is no problem. 101 2049 : for (int x=0; x<2048; ++x) 102 2048 : addr.push_back(b.alloc(x+1)); 103 2049 : for (int x=0; x<2048; ++x) 104 2048 : b.free(addr[((x*23)%2048)^242]); 105 1 : addr.clear(); 106 : 107 : // Go entirely wild: free and alloc interleaved, 108 : // generate targets and sizes using pseudo-randomness. 109 2049 : for (int x=0; x<2048; ++x) 110 2048 : addr.push_back(nullptr); 111 1 : uint32_t s = 0x12345678; 112 5001 : for (int x=0; x<5000; ++x) { 113 5000 : int idx = s & (addr.size()-1); 114 5000 : if (s & 0x80000000) { 115 2458 : b.free(addr[idx]); 116 2458 : addr[idx] = nullptr; 117 5000 : } else if(!addr[idx]) { 118 1741 : addr[idx] = b.alloc((s >> 16) & 2047); 119 1741 : } 120 5000 : bool lsb = s & 1; 121 5000 : s >>= 1; 122 5000 : if (lsb) 123 2458 : s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0 124 5000 : } 125 2049 : for (void *ptr: addr) 126 2048 : b.free(ptr); 127 1 : addr.clear(); 128 : 129 1 : BOOST_CHECK(b.stats().total == synth_size); 130 1 : BOOST_CHECK(b.stats().free == synth_size); 131 2 : } 132 : 133 : /** Mock LockedPageAllocator for testing */ 134 : class TestLockedPageAllocator: public LockedPageAllocator 135 : { 136 : public: 137 2 : TestLockedPageAllocator(int count_in, int lockedcount_in): count(count_in), lockedcount(lockedcount_in) {} 138 4 : void* AllocateLocked(size_t len, bool *lockingSuccess) override 139 : { 140 4 : *lockingSuccess = false; 141 4 : if (count > 0) { 142 3 : --count; 143 : 144 3 : if (lockedcount > 0) { 145 1 : --lockedcount; 146 1 : *lockingSuccess = true; 147 1 : } 148 : 149 3 : return reinterpret_cast<void*>(uint64_t{static_cast<uint64_t>(0x08000000) + (count << 24)}); // Fake address, do not actually use this memory 150 : } 151 1 : return nullptr; 152 4 : } 153 3 : void FreeLocked(void* addr, size_t len) override 154 : { 155 3 : } 156 1 : size_t GetLimit() override 157 : { 158 1 : return std::numeric_limits<size_t>::max(); 159 : } 160 : private: 161 : int count; 162 : int lockedcount; 163 : }; 164 : 165 148 : BOOST_AUTO_TEST_CASE(lockedpool_tests_mock) 166 : { 167 : // Test over three virtual arenas, of which one will succeed being locked 168 1 : std::unique_ptr<LockedPageAllocator> x = std::make_unique<TestLockedPageAllocator>(3, 1); 169 1 : LockedPool pool(std::move(x)); 170 1 : BOOST_CHECK(pool.stats().total == 0); 171 1 : BOOST_CHECK(pool.stats().locked == 0); 172 : 173 : // Ensure unreasonable requests are refused without allocating anything 174 1 : void *invalid_toosmall = pool.alloc(0); 175 1 : BOOST_CHECK(invalid_toosmall == nullptr); 176 1 : BOOST_CHECK(pool.stats().used == 0); 177 1 : BOOST_CHECK(pool.stats().free == 0); 178 1 : void *invalid_toobig = pool.alloc(LockedPool::ARENA_SIZE+1); 179 1 : BOOST_CHECK(invalid_toobig == nullptr); 180 1 : BOOST_CHECK(pool.stats().used == 0); 181 1 : BOOST_CHECK(pool.stats().free == 0); 182 : 183 1 : void *a0 = pool.alloc(LockedPool::ARENA_SIZE / 2); 184 1 : BOOST_CHECK(a0); 185 1 : BOOST_CHECK(pool.stats().locked == LockedPool::ARENA_SIZE); 186 1 : void *a1 = pool.alloc(LockedPool::ARENA_SIZE / 2); 187 1 : BOOST_CHECK(a1); 188 1 : void *a2 = pool.alloc(LockedPool::ARENA_SIZE / 2); 189 1 : BOOST_CHECK(a2); 190 1 : void *a3 = pool.alloc(LockedPool::ARENA_SIZE / 2); 191 1 : BOOST_CHECK(a3); 192 1 : void *a4 = pool.alloc(LockedPool::ARENA_SIZE / 2); 193 1 : BOOST_CHECK(a4); 194 1 : void *a5 = pool.alloc(LockedPool::ARENA_SIZE / 2); 195 1 : BOOST_CHECK(a5); 196 : // We've passed a count of three arenas, so this allocation should fail 197 1 : void *a6 = pool.alloc(16); 198 1 : BOOST_CHECK(!a6); 199 : 200 1 : pool.free(a0); 201 1 : pool.free(a2); 202 1 : pool.free(a4); 203 1 : pool.free(a1); 204 1 : pool.free(a3); 205 1 : pool.free(a5); 206 1 : BOOST_CHECK(pool.stats().total == 3*LockedPool::ARENA_SIZE); 207 1 : BOOST_CHECK(pool.stats().locked == LockedPool::ARENA_SIZE); 208 1 : BOOST_CHECK(pool.stats().used == 0); 209 1 : } 210 : 211 : // These tests used the live LockedPoolManager object, this is also used 212 : // by other tests so the conditions are somewhat less controllable and thus the 213 : // tests are somewhat more error-prone. 214 148 : BOOST_AUTO_TEST_CASE(lockedpool_tests_live) 215 : { 216 1 : LockedPoolManager &pool = LockedPoolManager::Instance(); 217 1 : LockedPool::Stats initial = pool.stats(); 218 : 219 1 : void *a0 = pool.alloc(16); 220 1 : BOOST_CHECK(a0); 221 : // Test reading and writing the allocated memory 222 1 : *((uint32_t*)a0) = 0x1234; 223 1 : BOOST_CHECK(*((uint32_t*)a0) == 0x1234); 224 : 225 1 : pool.free(a0); 226 : try { // Test exception on double-free 227 1 : pool.free(a0); 228 0 : BOOST_CHECK(0); 229 1 : } catch(std::runtime_error &) 230 : { 231 1 : } 232 : // If more than one new arena was allocated for the above tests, something is wrong 233 1 : BOOST_CHECK(pool.stats().total <= (initial.total + LockedPool::ARENA_SIZE)); 234 : // Usage must be back to where it started 235 1 : BOOST_CHECK(pool.stats().used == initial.used); 236 2 : } 237 : 238 148 : BOOST_AUTO_TEST_CASE(mt_pooled_secure_allocator_zero_pool_count) 239 : { 240 1 : mt_pooled_secure_allocator<unsigned char> allocator{/*nrequested_size=*/32, 241 : /*nnext_size=*/32, 242 : /*nmax_size=*/0, 243 : /*pools_count=*/0}; 244 : 245 1 : auto* p = allocator.allocate(1); 246 1 : BOOST_REQUIRE(p != nullptr); 247 1 : *p = 0x42; 248 1 : BOOST_CHECK_EQUAL(*p, 0x42); 249 1 : allocator.deallocate(p, 1); 250 1 : } 251 : 252 146 : BOOST_AUTO_TEST_SUITE_END()