123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/allocator/partition_allocator/address_pool_manager.h"
- #include <cstdint>
- #include "base/allocator/partition_allocator/address_space_stats.h"
- #include "base/allocator/partition_allocator/page_allocator.h"
- #include "base/allocator/partition_allocator/partition_alloc_base/bits.h"
- #include "base/allocator/partition_allocator/partition_alloc_constants.h"
- #include "build/build_config.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace partition_alloc::internal {
- class AddressSpaceStatsDumperForTesting final : public AddressSpaceStatsDumper {
- public:
- AddressSpaceStatsDumperForTesting() = default;
- ~AddressSpaceStatsDumperForTesting() = default;
- void DumpStats(
- const partition_alloc::AddressSpaceStats* address_space_stats) override {
- regular_pool_usage_ = address_space_stats->regular_pool_stats.usage;
- #if defined(PA_HAS_64_BITS_POINTERS)
- regular_pool_largest_reservation_ =
- address_space_stats->regular_pool_stats.largest_available_reservation;
- #endif // defined(PA_HAS_64_BITS_POINTERS)
- #if !defined(PA_HAS_64_BITS_POINTERS) && BUILDFLAG(USE_BACKUP_REF_PTR)
- blocklist_size_ = address_space_stats->blocklist_size;
- #endif // !defined(PA_HAS_64_BITS_POINTERS) && BUILDFLAG(USE_BACKUP_REF_PTR)
- }
- size_t regular_pool_usage_ = 0;
- size_t regular_pool_largest_reservation_ = 0;
- size_t blocklist_size_ = 0;
- };
- #if defined(PA_HAS_64_BITS_POINTERS)
- class AddressPoolManagerForTesting : public AddressPoolManager {
- public:
- AddressPoolManagerForTesting() = default;
- ~AddressPoolManagerForTesting() = default;
- };
- class PartitionAllocAddressPoolManagerTest : public testing::Test {
- protected:
- PartitionAllocAddressPoolManagerTest() = default;
- ~PartitionAllocAddressPoolManagerTest() override = default;
- void SetUp() override {
- manager_ = std::make_unique<AddressPoolManagerForTesting>();
- base_address_ = AllocPages(kPoolSize, kSuperPageSize,
- PageAccessibilityConfiguration::kInaccessible,
- PageTag::kPartitionAlloc);
- ASSERT_TRUE(base_address_);
- pool_ = manager_->Add(base_address_, kPoolSize);
- }
- void TearDown() override {
- manager_->Remove(pool_);
- FreePages(base_address_, kPoolSize);
- manager_.reset();
- }
- AddressPoolManager* GetAddressPoolManager() { return manager_.get(); }
- static constexpr size_t kPoolSize = kPoolMaxSize;
- static constexpr size_t kPageCnt = kPoolSize / kSuperPageSize;
- std::unique_ptr<AddressPoolManagerForTesting> manager_;
- uintptr_t base_address_;
- pool_handle pool_;
- };
- TEST_F(PartitionAllocAddressPoolManagerTest, TooLargePool) {
- uintptr_t base_addr = 0x4200000;
- EXPECT_DEATH_IF_SUPPORTED(
- GetAddressPoolManager()->Add(base_addr, kPoolSize + kSuperPageSize), "");
- }
- TEST_F(PartitionAllocAddressPoolManagerTest, ManyPages) {
- EXPECT_EQ(
- GetAddressPoolManager()->Reserve(pool_, 0, kPageCnt * kSuperPageSize),
- base_address_);
- EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, base_address_,
- kPageCnt * kSuperPageSize);
- EXPECT_EQ(
- GetAddressPoolManager()->Reserve(pool_, 0, kPageCnt * kSuperPageSize),
- base_address_);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, base_address_,
- kPageCnt * kSuperPageSize);
- }
- TEST_F(PartitionAllocAddressPoolManagerTest, PagesFragmented) {
- uintptr_t addrs[kPageCnt];
- for (size_t i = 0; i < kPageCnt; ++i) {
- addrs[i] = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize);
- EXPECT_EQ(addrs[i], base_address_ + i * kSuperPageSize);
- }
- EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u);
- // Free other other super page, so that we have plenty of free space, but none
- // of the empty spaces can fit 2 super pages.
- for (size_t i = 1; i < kPageCnt; i += 2) {
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, addrs[i],
- kSuperPageSize);
- }
- EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize), 0u);
- // Reserve freed super pages back, so that there are no free ones.
- for (size_t i = 1; i < kPageCnt; i += 2) {
- addrs[i] = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize);
- EXPECT_EQ(addrs[i], base_address_ + i * kSuperPageSize);
- }
- EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u);
- // Lastly, clean up.
- for (uintptr_t addr : addrs) {
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, addr, kSuperPageSize);
- }
- }
- TEST_F(PartitionAllocAddressPoolManagerTest, GetUsedSuperpages) {
- uintptr_t addrs[kPageCnt];
- for (size_t i = 0; i < kPageCnt; ++i) {
- addrs[i] = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize);
- EXPECT_EQ(addrs[i], base_address_ + i * kSuperPageSize);
- }
- EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize), 0u);
- std::bitset<kMaxSuperPagesInPool> used_super_pages;
- GetAddressPoolManager()->GetPoolUsedSuperPages(pool_, used_super_pages);
- // We expect every bit to be set.
- for (size_t i = 0; i < kPageCnt; ++i) {
- ASSERT_TRUE(used_super_pages.test(i));
- }
- // Free every other super page, so that we have plenty of free space, but none
- // of the empty spaces can fit 2 super pages.
- for (size_t i = 1; i < kPageCnt; i += 2) {
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, addrs[i],
- kSuperPageSize);
- }
- EXPECT_EQ(GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize), 0u);
- GetAddressPoolManager()->GetPoolUsedSuperPages(pool_, used_super_pages);
- // We expect every other bit to be set.
- for (size_t i = 0; i < kPageCnt; i++) {
- if (i % 2 == 0) {
- ASSERT_TRUE(used_super_pages.test(i));
- } else {
- ASSERT_FALSE(used_super_pages.test(i));
- }
- }
- // Free the even numbered super pages.
- for (size_t i = 0; i < kPageCnt; i += 2) {
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, addrs[i],
- kSuperPageSize);
- }
- // Finally check to make sure all bits are zero in the used superpage bitset.
- GetAddressPoolManager()->GetPoolUsedSuperPages(pool_, used_super_pages);
- for (size_t i = 0; i < kPageCnt; i++) {
- ASSERT_FALSE(used_super_pages.test(i));
- }
- }
- TEST_F(PartitionAllocAddressPoolManagerTest, IrregularPattern) {
- uintptr_t a1 = GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize);
- EXPECT_EQ(a1, base_address_);
- uintptr_t a2 = GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize);
- EXPECT_EQ(a2, base_address_ + 1 * kSuperPageSize);
- uintptr_t a3 = GetAddressPoolManager()->Reserve(pool_, 0, 3 * kSuperPageSize);
- EXPECT_EQ(a3, base_address_ + 3 * kSuperPageSize);
- uintptr_t a4 = GetAddressPoolManager()->Reserve(pool_, 0, 4 * kSuperPageSize);
- EXPECT_EQ(a4, base_address_ + 6 * kSuperPageSize);
- uintptr_t a5 = GetAddressPoolManager()->Reserve(pool_, 0, 5 * kSuperPageSize);
- EXPECT_EQ(a5, base_address_ + 10 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a4, 4 * kSuperPageSize);
- uintptr_t a6 = GetAddressPoolManager()->Reserve(pool_, 0, 6 * kSuperPageSize);
- EXPECT_EQ(a6, base_address_ + 15 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a5, 5 * kSuperPageSize);
- uintptr_t a7 = GetAddressPoolManager()->Reserve(pool_, 0, 7 * kSuperPageSize);
- EXPECT_EQ(a7, base_address_ + 6 * kSuperPageSize);
- uintptr_t a8 = GetAddressPoolManager()->Reserve(pool_, 0, 3 * kSuperPageSize);
- EXPECT_EQ(a8, base_address_ + 21 * kSuperPageSize);
- uintptr_t a9 = GetAddressPoolManager()->Reserve(pool_, 0, 2 * kSuperPageSize);
- EXPECT_EQ(a9, base_address_ + 13 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a7, 7 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a9, 2 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a6, 6 * kSuperPageSize);
- uintptr_t a10 =
- GetAddressPoolManager()->Reserve(pool_, 0, 15 * kSuperPageSize);
- EXPECT_EQ(a10, base_address_ + 6 * kSuperPageSize);
- // Clean up.
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a1, kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a2, 2 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a3, 3 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a8, 3 * kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, a10,
- 15 * kSuperPageSize);
- }
- TEST_F(PartitionAllocAddressPoolManagerTest, DecommittedDataIsErased) {
- uintptr_t address =
- GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize);
- ASSERT_TRUE(address);
- RecommitSystemPages(address, kSuperPageSize,
- PageAccessibilityConfiguration::kReadWrite,
- PageAccessibilityDisposition::kRequireUpdate);
- memset(reinterpret_cast<void*>(address), 42, kSuperPageSize);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, address, kSuperPageSize);
- uintptr_t address2 =
- GetAddressPoolManager()->Reserve(pool_, 0, kSuperPageSize);
- ASSERT_EQ(address, address2);
- RecommitSystemPages(address2, kSuperPageSize,
- PageAccessibilityConfiguration::kReadWrite,
- PageAccessibilityDisposition::kRequireUpdate);
- uint32_t sum = 0;
- for (size_t i = 0; i < kSuperPageSize; i++) {
- sum += reinterpret_cast<uint8_t*>(address2)[i];
- }
- EXPECT_EQ(0u, sum) << sum / 42 << " bytes were not zeroed";
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, address2,
- kSuperPageSize);
- }
- TEST_F(PartitionAllocAddressPoolManagerTest, RegularPoolUsageChanges) {
- AddressSpaceStatsDumperForTesting dumper{};
- GetAddressPoolManager()->DumpStats(&dumper);
- ASSERT_EQ(dumper.regular_pool_usage_, 0ull);
- ASSERT_EQ(dumper.regular_pool_largest_reservation_, kPageCnt);
- // Bisect the pool by reserving a super page in the middle.
- const uintptr_t midpoint_address =
- base_address_ + (kPageCnt / 2) * kSuperPageSize;
- ASSERT_EQ(
- GetAddressPoolManager()->Reserve(pool_, midpoint_address, kSuperPageSize),
- midpoint_address);
- GetAddressPoolManager()->DumpStats(&dumper);
- ASSERT_EQ(dumper.regular_pool_usage_, 1ull);
- ASSERT_EQ(dumper.regular_pool_largest_reservation_, kPageCnt / 2);
- GetAddressPoolManager()->UnreserveAndDecommit(pool_, midpoint_address,
- kSuperPageSize);
- GetAddressPoolManager()->DumpStats(&dumper);
- ASSERT_EQ(dumper.regular_pool_usage_, 0ull);
- ASSERT_EQ(dumper.regular_pool_largest_reservation_, kPageCnt);
- }
- #else // defined(PA_HAS_64_BITS_POINTERS)
- TEST(PartitionAllocAddressPoolManagerTest, IsManagedByRegularPool) {
- constexpr size_t kAllocCount = 8;
- static const size_t kNumPages[kAllocCount] = {1, 4, 7, 8, 13, 16, 31, 60};
- uintptr_t addrs[kAllocCount];
- for (size_t i = 0; i < kAllocCount; ++i) {
- addrs[i] = AddressPoolManager::GetInstance().Reserve(
- GetRegularPool(), 0,
- AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap *
- kNumPages[i]);
- EXPECT_TRUE(addrs[i]);
- EXPECT_TRUE(!(addrs[i] & kSuperPageOffsetMask));
- AddressPoolManager::GetInstance().MarkUsed(
- GetRegularPool(), addrs[i],
- AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap *
- kNumPages[i]);
- }
- for (size_t i = 0; i < kAllocCount; ++i) {
- uintptr_t address = addrs[i];
- size_t num_pages =
- base::bits::AlignUp(
- kNumPages[i] *
- AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap,
- kSuperPageSize) /
- AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap;
- for (size_t j = 0; j < num_pages; ++j) {
- if (j < kNumPages[i]) {
- EXPECT_TRUE(AddressPoolManager::IsManagedByRegularPool(address));
- } else {
- EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(address));
- }
- EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(address));
- address += AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap;
- }
- }
- for (size_t i = 0; i < kAllocCount; ++i) {
- AddressPoolManager::GetInstance().MarkUnused(
- GetRegularPool(), addrs[i],
- AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap *
- kNumPages[i]);
- AddressPoolManager::GetInstance().UnreserveAndDecommit(
- GetRegularPool(), addrs[i],
- AddressPoolManagerBitmap::kBytesPer1BitOfRegularPoolBitmap *
- kNumPages[i]);
- EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(addrs[i]));
- EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(addrs[i]));
- }
- }
- #if BUILDFLAG(USE_BACKUP_REF_PTR)
- TEST(PartitionAllocAddressPoolManagerTest, IsManagedByBRPPool) {
- constexpr size_t kAllocCount = 4;
- // Totally (1+3+7+11) * 2MB = 44MB allocation
- static const size_t kNumPages[kAllocCount] = {1, 3, 7, 11};
- uintptr_t addrs[kAllocCount];
- for (size_t i = 0; i < kAllocCount; ++i) {
- addrs[i] = AddressPoolManager::GetInstance().Reserve(
- GetBRPPool(), 0, kSuperPageSize * kNumPages[i]);
- EXPECT_TRUE(addrs[i]);
- EXPECT_TRUE(!(addrs[i] & kSuperPageOffsetMask));
- AddressPoolManager::GetInstance().MarkUsed(GetBRPPool(), addrs[i],
- kSuperPageSize * kNumPages[i]);
- }
- constexpr size_t first_guard_size =
- AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap *
- AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap;
- constexpr size_t last_guard_size =
- AddressPoolManagerBitmap::kBytesPer1BitOfBRPPoolBitmap *
- (AddressPoolManagerBitmap::kGuardBitsOfBRPPoolBitmap -
- AddressPoolManagerBitmap::kGuardOffsetOfBRPPoolBitmap);
- for (size_t i = 0; i < kAllocCount; ++i) {
- uintptr_t address = addrs[i];
- size_t num_allocated_size = kNumPages[i] * kSuperPageSize;
- size_t num_system_pages = num_allocated_size / SystemPageSize();
- for (size_t j = 0; j < num_system_pages; ++j) {
- size_t offset = address - addrs[i];
- if (offset < first_guard_size ||
- offset >= (num_allocated_size - last_guard_size)) {
- EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(address));
- } else {
- EXPECT_TRUE(AddressPoolManager::IsManagedByBRPPool(address));
- }
- EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(address));
- address += SystemPageSize();
- }
- }
- for (size_t i = 0; i < kAllocCount; ++i) {
- AddressPoolManager::GetInstance().MarkUnused(GetBRPPool(), addrs[i],
- kSuperPageSize * kNumPages[i]);
- AddressPoolManager::GetInstance().UnreserveAndDecommit(
- GetBRPPool(), addrs[i], kSuperPageSize * kNumPages[i]);
- EXPECT_FALSE(AddressPoolManager::IsManagedByRegularPool(addrs[i]));
- EXPECT_FALSE(AddressPoolManager::IsManagedByBRPPool(addrs[i]));
- }
- }
- #endif // BUILDFLAG(USE_BACKUP_REF_PTR)
- TEST(PartitionAllocAddressPoolManagerTest, RegularPoolUsageChanges) {
- AddressSpaceStatsDumperForTesting dumper{};
- AddressPoolManager::GetInstance().DumpStats(&dumper);
- const size_t usage_before = dumper.regular_pool_usage_;
- const uintptr_t address = AddressPoolManager::GetInstance().Reserve(
- GetRegularPool(), 0, kSuperPageSize);
- ASSERT_TRUE(address);
- AddressPoolManager::GetInstance().MarkUsed(GetRegularPool(), address,
- kSuperPageSize);
- AddressPoolManager::GetInstance().DumpStats(&dumper);
- EXPECT_GT(dumper.regular_pool_usage_, usage_before);
- AddressPoolManager::GetInstance().MarkUnused(GetRegularPool(), address,
- kSuperPageSize);
- AddressPoolManager::GetInstance().UnreserveAndDecommit(
- GetRegularPool(), address, kSuperPageSize);
- AddressPoolManager::GetInstance().DumpStats(&dumper);
- EXPECT_EQ(dumper.regular_pool_usage_, usage_before);
- }
- #endif // defined(PA_HAS_64_BITS_POINTERS)
- } // namespace partition_alloc::internal
|