1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "wtf/PartitionAlloc.h" 33 34 #include "wtf/CryptographicallyRandomNumber.h" 35 #include "wtf/OwnArrayPtr.h" 36 #include "wtf/PageAllocator.h" 37 #include <gtest/gtest.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #if OS(UNIX) 42 #include <sys/mman.h> 43 44 #ifndef MAP_ANONYMOUS 45 #define MAP_ANONYMOUS MAP_ANON 46 #endif 47 #endif // OS(UNIX) 48 49 #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) 50 51 namespace { 52 53 static PartitionRoot root; 54 55 static const int kTestAllocSize = sizeof(void*); 56 57 static void RandomNumberSource(unsigned char* buf, size_t len) 58 { 59 memset(buf, '\0', len); 60 } 61 62 static void TestSetup() 63 { 64 WTF::setRandomSource(RandomNumberSource); 65 partitionAllocInit(&root); 66 } 67 68 static void TestShutdown() 69 { 70 // We expect no leaks in the general case. We have a test for leak 71 // detection. 72 EXPECT_TRUE(partitionAllocShutdown(&root)); 73 } 74 75 static WTF::PartitionPageHeader* GetFullPage(size_t size) 76 { 77 size_t bucketIdx = size >> WTF::kBucketShift; 78 WTF::PartitionBucket* bucket = &root.buckets[bucketIdx]; 79 size_t numSlots = (WTF::kPartitionPageSize - sizeof(WTF::PartitionPageHeader)) / size; 80 void* first = 0; 81 void* last = 0; 82 size_t i; 83 for (i = 0; i < numSlots; ++i) { 84 void* ptr = partitionAlloc(&root, size); 85 EXPECT_TRUE(ptr); 86 if (!i) 87 first = ptr; 88 else if (i == numSlots - 1) 89 last = ptr; 90 } 91 EXPECT_EQ(reinterpret_cast<size_t>(first) & WTF::kPartitionPageBaseMask, reinterpret_cast<size_t>(last) & WTF::kPartitionPageBaseMask); 92 EXPECT_EQ(numSlots, static_cast<size_t>(bucket->currPage->numAllocatedSlots)); 93 EXPECT_EQ(0, bucket->currPage->freelistHead); 94 EXPECT_TRUE(bucket->currPage); 95 EXPECT_TRUE(bucket->currPage != &root.seedPage); 96 return bucket->currPage; 97 } 98 99 static void FreeFullPage(WTF::PartitionPageHeader* page, size_t size) 100 { 101 size_t numSlots = (WTF::kPartitionPageSize - sizeof(WTF::PartitionPageHeader)) / size; 102 EXPECT_EQ(numSlots, static_cast<size_t>(abs(page->numAllocatedSlots))); 103 char* ptr = reinterpret_cast<char*>(page); 104 ptr += sizeof(WTF::PartitionPageHeader); 105 size_t i; 106 for (i = 0; i < numSlots; ++i) { 107 partitionFree(ptr); 108 ptr += size; 109 } 110 EXPECT_EQ(0, page->numAllocatedSlots); 111 } 112 113 // Check that the most basic of allocate / free pairs work. 114 TEST(WTF_PartitionAlloc, Basic) 115 { 116 TestSetup(); 117 size_t bucketIdx = kTestAllocSize >> WTF::kBucketShift; 118 WTF::PartitionBucket* bucket = &root.buckets[bucketIdx]; 119 120 EXPECT_EQ(0, bucket->freePages); 121 EXPECT_EQ(&bucket->root->seedPage, bucket->currPage); 122 EXPECT_EQ(&bucket->root->seedPage, bucket->currPage->next); 123 EXPECT_EQ(&bucket->root->seedPage, bucket->currPage->prev); 124 125 void* ptr = partitionAlloc(&root, kTestAllocSize); 126 EXPECT_TRUE(ptr); 127 EXPECT_EQ(sizeof(WTF::PartitionPageHeader), reinterpret_cast<size_t>(ptr) & WTF::kPartitionPageOffsetMask); 128 // Check that the offset appears to include a guard page. 129 EXPECT_EQ(WTF::kPartitionPageSize + sizeof(WTF::PartitionPageHeader), reinterpret_cast<size_t>(ptr) & WTF::kSuperPageOffsetMask); 130 131 partitionFree(ptr); 132 // Expect that a just-freed page doesn't get tossed to the freelist. 133 EXPECT_EQ(0, bucket->freePages); 134 135 TestShutdown(); 136 } 137 138 // Check that we can detect a memory leak. 139 TEST(WTF_PartitionAlloc, SimpleLeak) 140 { 141 TestSetup(); 142 void* leakedPtr = partitionAlloc(&root, kTestAllocSize); 143 EXPECT_FALSE(partitionAllocShutdown(&root)); 144 } 145 146 // Test multiple allocations, and freelist handling. 147 TEST(WTF_PartitionAlloc, MultiAlloc) 148 { 149 TestSetup(); 150 151 char* ptr1 = reinterpret_cast<char*>(partitionAlloc(&root, kTestAllocSize)); 152 char* ptr2 = reinterpret_cast<char*>(partitionAlloc(&root, kTestAllocSize)); 153 EXPECT_TRUE(ptr1); 154 EXPECT_TRUE(ptr2); 155 ptrdiff_t diff = ptr2 - ptr1; 156 EXPECT_EQ(kTestAllocSize, diff); 157 158 // Check that we re-use the just-freed slot. 159 partitionFree(ptr2); 160 ptr2 = reinterpret_cast<char*>(partitionAlloc(&root, kTestAllocSize)); 161 EXPECT_TRUE(ptr2); 162 diff = ptr2 - ptr1; 163 EXPECT_EQ(kTestAllocSize, diff); 164 partitionFree(ptr1); 165 ptr1 = reinterpret_cast<char*>(partitionAlloc(&root, kTestAllocSize)); 166 EXPECT_TRUE(ptr1); 167 diff = ptr2 - ptr1; 168 EXPECT_EQ(kTestAllocSize, diff); 169 170 char* ptr3 = reinterpret_cast<char*>(partitionAlloc(&root, kTestAllocSize)); 171 EXPECT_TRUE(ptr3); 172 diff = ptr3 - ptr1; 173 EXPECT_EQ(kTestAllocSize * 2, diff); 174 175 partitionFree(ptr1); 176 partitionFree(ptr2); 177 partitionFree(ptr3); 178 179 TestShutdown(); 180 } 181 182 // Test a bucket with multiple pages. 183 TEST(WTF_PartitionAlloc, MultiPages) 184 { 185 TestSetup(); 186 size_t bucketIdx = kTestAllocSize >> WTF::kBucketShift; 187 WTF::PartitionBucket* bucket = &root.buckets[bucketIdx]; 188 189 WTF::PartitionPageHeader* page = GetFullPage(kTestAllocSize); 190 FreeFullPage(page, kTestAllocSize); 191 EXPECT_EQ(0, bucket->freePages); 192 EXPECT_EQ(page, bucket->currPage); 193 EXPECT_EQ(page, page->next); 194 EXPECT_EQ(page, page->prev); 195 196 page = GetFullPage(kTestAllocSize); 197 WTF::PartitionPageHeader* page2 = GetFullPage(kTestAllocSize); 198 199 EXPECT_EQ(page2, bucket->currPage); 200 201 // Fully free the non-current page, it should be freelisted. 202 FreeFullPage(page, kTestAllocSize); 203 EXPECT_EQ(0, page->numAllocatedSlots); 204 EXPECT_TRUE(bucket->freePages); 205 EXPECT_EQ(page, bucket->freePages->page); 206 EXPECT_EQ(page2, bucket->currPage); 207 208 // Allocate a new page, it should pull from the freelist. 209 page = GetFullPage(kTestAllocSize); 210 EXPECT_FALSE(bucket->freePages); 211 EXPECT_EQ(page, bucket->currPage); 212 213 FreeFullPage(page, kTestAllocSize); 214 FreeFullPage(page2, kTestAllocSize); 215 EXPECT_EQ(0, page->numAllocatedSlots); 216 EXPECT_EQ(0, page2->numAllocatedSlots); 217 218 TestShutdown(); 219 } 220 221 // Test some finer aspects of internal page transitions. 222 TEST(WTF_PartitionAlloc, PageTransitions) 223 { 224 TestSetup(); 225 size_t bucketIdx = kTestAllocSize >> WTF::kBucketShift; 226 WTF::PartitionBucket* bucket = &root.buckets[bucketIdx]; 227 228 WTF::PartitionPageHeader* page1 = GetFullPage(kTestAllocSize); 229 WTF::PartitionPageHeader* page2 = GetFullPage(kTestAllocSize); 230 EXPECT_EQ(page2, bucket->currPage); 231 EXPECT_EQ(page1, page2->next); 232 EXPECT_EQ(page1, page2->prev); 233 // Allocating another page at this point should cause us to scan over page1 234 // (which is both full and NOT our current page), and evict it from the 235 // freelist. Older code had a O(n^2) condition due to failure to do this. 236 WTF::PartitionPageHeader* page3 = GetFullPage(kTestAllocSize); 237 EXPECT_EQ(page3, bucket->currPage); 238 EXPECT_EQ(page2, page3->next); 239 EXPECT_EQ(page3, page2->next); 240 241 // Work out a pointer into page2 and free it. 242 char* ptr = reinterpret_cast<char*>(page2) + sizeof(WTF::PartitionPageHeader); 243 partitionFree(ptr); 244 // Trying to allocate at this time should cause us to cycle around to page2 245 // and find the recently freed slot. 246 char* newPtr = reinterpret_cast<char*>(partitionAlloc(&root, kTestAllocSize)); 247 EXPECT_EQ(ptr, newPtr); 248 EXPECT_EQ(page2, bucket->currPage); 249 250 // Work out a pointer into page1 and free it. This should pull the page 251 // back into the ring list of available pages. 252 ptr = reinterpret_cast<char*>(page1) + sizeof(WTF::PartitionPageHeader); 253 partitionFree(ptr); 254 // This allocation should be satisfied by page1. 255 newPtr = reinterpret_cast<char*>(partitionAlloc(&root, kTestAllocSize)); 256 EXPECT_EQ(ptr, newPtr); 257 EXPECT_EQ(page1, bucket->currPage); 258 259 FreeFullPage(page3, kTestAllocSize); 260 FreeFullPage(page2, kTestAllocSize); 261 FreeFullPage(page1, kTestAllocSize); 262 263 TestShutdown(); 264 } 265 266 // Test some corner cases relating to page transitions in the internal 267 // free page list metadata bucket. 268 TEST(WTF_PartitionAlloc, FreePageListPageTransitions) 269 { 270 TestSetup(); 271 WTF::PartitionBucket* freePageBucket = &root.buckets[WTF::kFreePageBucket]; 272 size_t bucketIdx = kTestAllocSize >> WTF::kBucketShift; 273 WTF::PartitionBucket* bucket = &root.buckets[bucketIdx]; 274 275 size_t numToFillFreeListPage = (WTF::kPartitionPageSize - sizeof(WTF::PartitionPageHeader)) / sizeof(WTF::PartitionFreepagelistEntry); 276 OwnArrayPtr<WTF::PartitionPageHeader*> pages = adoptArrayPtr(new WTF::PartitionPageHeader*[numToFillFreeListPage + 1]); 277 size_t i; 278 // The +1 is because we need to account for the fact that the current page 279 // never gets thrown on the freelist. 280 for (i = 0; i < numToFillFreeListPage + 1; ++i) { 281 pages[i] = GetFullPage(kTestAllocSize); 282 } 283 EXPECT_EQ(pages[numToFillFreeListPage], bucket->currPage); 284 for (i = 0; i < numToFillFreeListPage + 1; ++i) { 285 FreeFullPage(pages[i], kTestAllocSize); 286 } 287 EXPECT_EQ(pages[numToFillFreeListPage], bucket->currPage); 288 289 // At this moment, we should have filled an entire partition page full of 290 // WTF::PartitionFreepagelistEntry, in the special free list entry bucket. 291 EXPECT_EQ(numToFillFreeListPage, freePageBucket->currPage->numAllocatedSlots); 292 EXPECT_EQ(0, freePageBucket->currPage->freelistHead); 293 294 // Allocate / free a full couple of pages of a different bucket size so 295 // we get control of a different free page list. 296 WTF::PartitionPageHeader* page1 = GetFullPage(kTestAllocSize * 2); 297 WTF::PartitionPageHeader* page2 = GetFullPage(kTestAllocSize * 2); 298 FreeFullPage(page1, kTestAllocSize * 2); 299 FreeFullPage(page2, kTestAllocSize * 2); 300 301 // Now, we have a second page for free page objects, with a single entry 302 // in it -- from a free page in the "kTestAllocSize * 2" bucket. 303 EXPECT_EQ(1, freePageBucket->currPage->numAllocatedSlots); 304 EXPECT_EQ(0, freePageBucket->freePages); 305 306 // If we re-allocate all kTestAllocSize allocations, we'll pull all the 307 // free pages and end up freeing the first page for free page objects. 308 // It's getting a bit tricky but a nice re-entrancy is going on: 309 // alloc(kTestAllocSize) -> pulls page from free page list -> 310 // free(PartitionFreepagelistEntry) -> last entry in page freed -> 311 // alloc(PartitionFreepagelistEntry). 312 for (i = 0; i < numToFillFreeListPage + 1; ++i) { 313 pages[i] = GetFullPage(kTestAllocSize); 314 } 315 EXPECT_EQ(pages[numToFillFreeListPage], bucket->currPage); 316 EXPECT_EQ(2, freePageBucket->currPage->numAllocatedSlots); 317 EXPECT_TRUE(freePageBucket->freePages); 318 319 // As part of the final free-up, we'll test another re-entrancy: 320 // free(kTestAllocSize) -> last entry in page freed -> 321 // alloc(PartitionFreepagelistEntry) -> pulls page from free page list -> 322 // free(PartitionFreepagelistEntry) 323 for (i = 0; i < numToFillFreeListPage + 1; ++i) { 324 FreeFullPage(pages[i], kTestAllocSize); 325 } 326 EXPECT_EQ(pages[numToFillFreeListPage], bucket->currPage); 327 328 TestShutdown(); 329 } 330 331 // Test a large series of allocations that cross more than one underlying 332 // 64KB super page allocation. 333 TEST(WTF_PartitionAlloc, MultiPageAllocs) 334 { 335 TestSetup(); 336 // This is guaranteed to cross a super page boundary because the first 337 // partition page "slot" will be taken up by a guard page. 338 size_t numPagesNeeded = WTF::kSuperPageSize / WTF::kPartitionPageSize; 339 EXPECT_GT(numPagesNeeded, 1u); 340 OwnArrayPtr<WTF::PartitionPageHeader*> pages; 341 pages = adoptArrayPtr(new WTF::PartitionPageHeader*[numPagesNeeded]); 342 uintptr_t firstSuperPageBase = 0; 343 size_t i; 344 for (i = 0; i < numPagesNeeded; ++i) { 345 pages[i] = GetFullPage(kTestAllocSize); 346 if (!i) 347 firstSuperPageBase = (reinterpret_cast<uintptr_t>(pages[i]) & WTF::kSuperPageBaseMask); 348 if (i == numPagesNeeded - 1) { 349 uintptr_t secondSuperPageBase = reinterpret_cast<uintptr_t>(pages[i]) & WTF::kSuperPageBaseMask; 350 EXPECT_FALSE(secondSuperPageBase == firstSuperPageBase); 351 // If the two super pages are contiguous, also check that we didn't 352 // erroneously allocate a guard page for the second page. 353 if (secondSuperPageBase == firstSuperPageBase + WTF::kSuperPageSize) 354 EXPECT_EQ(0u, secondSuperPageBase & WTF::kSuperPageOffsetMask); 355 } 356 } 357 for (i = 0; i < numPagesNeeded; ++i) { 358 FreeFullPage(pages[i], kTestAllocSize); 359 } 360 361 TestShutdown(); 362 } 363 364 // Test the generic allocation functions that can handle arbitrary sizes and 365 // reallocing etc. 366 TEST(WTF_PartitionAlloc, GenericAlloc) 367 { 368 TestSetup(); 369 370 void* ptr = partitionAllocGeneric(&root, 1); 371 EXPECT_TRUE(ptr); 372 partitionFreeGeneric(ptr, 1); 373 ptr = partitionAllocGeneric(&root, WTF::kMaxAllocation + 1); 374 EXPECT_TRUE(ptr); 375 partitionFreeGeneric(ptr, WTF::kMaxAllocation + 1); 376 377 ptr = partitionAllocGeneric(&root, 1); 378 EXPECT_TRUE(ptr); 379 void* origPtr = ptr; 380 char* charPtr = static_cast<char*>(ptr); 381 *charPtr = 'A'; 382 383 // Change the size of the realloc, remaining inside the same bucket. 384 void* newPtr = partitionReallocGeneric(&root, ptr, 1, 2); 385 EXPECT_EQ(ptr, newPtr); 386 newPtr = partitionReallocGeneric(&root, ptr, 2, 1); 387 EXPECT_EQ(ptr, newPtr); 388 newPtr = partitionReallocGeneric(&root, ptr, 1, WTF::kAllocationGranularity); 389 EXPECT_EQ(ptr, newPtr); 390 391 // Change the size of the realloc, switching buckets. 392 newPtr = partitionReallocGeneric(&root, ptr, WTF::kAllocationGranularity, WTF::kAllocationGranularity + 1); 393 EXPECT_NE(newPtr, ptr); 394 // Check that the realloc copied correctly. 395 char* newCharPtr = static_cast<char*>(newPtr); 396 EXPECT_EQ(*newCharPtr, 'A'); 397 *newCharPtr = 'B'; 398 // The realloc moved. To check that the old allocation was freed, we can 399 // do an alloc of the old allocation size and check that the old allocation 400 // address is at the head of the freelist and reused. 401 void* reusedPtr = partitionAllocGeneric(&root, 1); 402 EXPECT_EQ(reusedPtr, origPtr); 403 partitionFreeGeneric(reusedPtr, 1); 404 405 // Downsize the realloc. 406 ptr = newPtr; 407 newPtr = partitionReallocGeneric(&root, ptr, WTF::kAllocationGranularity + 1, 1); 408 EXPECT_EQ(newPtr, origPtr); 409 newCharPtr = static_cast<char*>(newPtr); 410 EXPECT_EQ(*newCharPtr, 'B'); 411 *newCharPtr = 'C'; 412 413 // Upsize the realloc to outside the partition. 414 ptr = newPtr; 415 newPtr = partitionReallocGeneric(&root, ptr, 1, WTF::kMaxAllocation + 1); 416 EXPECT_NE(newPtr, ptr); 417 newCharPtr = static_cast<char*>(newPtr); 418 EXPECT_EQ(*newCharPtr, 'C'); 419 *newCharPtr = 'D'; 420 421 // Upsize and downsize the realloc, remaining outside the partition. 422 ptr = newPtr; 423 newPtr = partitionReallocGeneric(&root, ptr, WTF::kMaxAllocation + 1, WTF::kMaxAllocation * 10); 424 newCharPtr = static_cast<char*>(newPtr); 425 EXPECT_EQ(*newCharPtr, 'D'); 426 *newCharPtr = 'E'; 427 ptr = newPtr; 428 newPtr = partitionReallocGeneric(&root, ptr, WTF::kMaxAllocation * 10, WTF::kMaxAllocation * 2); 429 newCharPtr = static_cast<char*>(newPtr); 430 EXPECT_EQ(*newCharPtr, 'E'); 431 *newCharPtr = 'F'; 432 433 // Downsize the realloc to inside the partition. 434 ptr = newPtr; 435 newPtr = partitionReallocGeneric(&root, ptr, WTF::kMaxAllocation * 2, 1); 436 EXPECT_NE(newPtr, ptr); 437 EXPECT_EQ(newPtr, origPtr); 438 newCharPtr = static_cast<char*>(newPtr); 439 EXPECT_EQ(*newCharPtr, 'F'); 440 441 partitionFreeGeneric(newPtr, 1); 442 TestShutdown(); 443 } 444 445 #if OS(UNIX) 446 447 // Test correct handling if our mapping collides with another. 448 TEST(WTF_PartitionAlloc, MappingCollision) 449 { 450 TestSetup(); 451 452 WTF::PartitionPageHeader* page1 = GetFullPage(kTestAllocSize); 453 char* pageBase = reinterpret_cast<char*>(page1); 454 // Map a single system page either side of the mapping for our allocations, 455 // with the goal of tripping up alignment of the next mapping. 456 void* map1 = mmap(pageBase - WTF::kSystemPageSize, WTF::kSystemPageSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 457 EXPECT_TRUE(map1 && map1 != MAP_FAILED); 458 void* map2 = mmap(pageBase + WTF::kSuperPageSize, WTF::kSystemPageSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 459 EXPECT_TRUE(map2 && map2 != MAP_FAILED); 460 461 WTF::PartitionPageHeader* page2 = GetFullPage(kTestAllocSize); 462 EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(page2) & WTF::kPartitionPageOffsetMask); 463 FreeFullPage(page2, kTestAllocSize); 464 465 FreeFullPage(page1, kTestAllocSize); 466 munmap(map1, WTF::kSystemPageSize); 467 munmap(map2, WTF::kSystemPageSize); 468 469 TestShutdown(); 470 } 471 472 #endif // OS(UNIX) 473 474 } // namespace 475 476 #endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR) 477