hardening_unittest.cc 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <cstdint>
  5. #include <string>
  6. #include <vector>
  7. #include "base/allocator/partition_allocator/partition_alloc_config.h"
  8. #include "base/allocator/partition_allocator/partition_freelist_entry.h"
  9. #include "base/allocator/partition_allocator/partition_page.h"
  10. #include "base/allocator/partition_allocator/partition_root.h"
  11. #include "build/build_config.h"
  12. #include "testing/gtest/include/gtest/gtest.h"
  13. // With *SAN, PartitionAlloc is rerouted to malloc().
  14. #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
  15. namespace partition_alloc::internal {
  16. namespace {
  17. // Death tests misbehave on Android, crbug.com/1240184
  18. #if !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) && \
  19. defined(PA_HAS_FREELIST_SHADOW_ENTRY)
  20. TEST(HardeningTest, PartialCorruption) {
  21. std::string important_data("very important");
  22. char* to_corrupt = const_cast<char*>(important_data.c_str());
  23. PartitionRoot<ThreadSafe> root({
  24. PartitionOptions::AlignedAlloc::kAllowed,
  25. PartitionOptions::ThreadCache::kDisabled,
  26. PartitionOptions::Quarantine::kDisallowed,
  27. PartitionOptions::Cookie::kDisallowed,
  28. PartitionOptions::BackupRefPtr::kDisabled,
  29. PartitionOptions::BackupRefPtrZapping::kDisabled,
  30. PartitionOptions::UseConfigurablePool::kNo,
  31. });
  32. root.UncapEmptySlotSpanMemoryForTesting();
  33. const size_t kAllocSize = 100;
  34. void* data = root.Alloc(kAllocSize, "");
  35. void* data2 = root.Alloc(kAllocSize, "");
  36. root.Free(data2);
  37. root.Free(data);
  38. // root->bucket->active_slot_span_head->freelist_head points to data, next_
  39. // points to data2. We can corrupt *data to get overwrite the next_ pointer.
  40. // Even if it looks reasonable (valid encoded pointer), freelist corruption
  41. // detection will make the code crash, because shadow_ doesn't match
  42. // encoded_next_.
  43. PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
  44. to_corrupt, false);
  45. EXPECT_DEATH(root.Alloc(kAllocSize, ""), "");
  46. }
  47. TEST(HardeningTest, OffHeapPointerCrashing) {
  48. std::string important_data("very important");
  49. char* to_corrupt = const_cast<char*>(important_data.c_str());
  50. PartitionRoot<ThreadSafe> root({
  51. PartitionOptions::AlignedAlloc::kAllowed,
  52. PartitionOptions::ThreadCache::kDisabled,
  53. PartitionOptions::Quarantine::kDisallowed,
  54. PartitionOptions::Cookie::kDisallowed,
  55. PartitionOptions::BackupRefPtr::kDisabled,
  56. PartitionOptions::BackupRefPtrZapping::kDisabled,
  57. PartitionOptions::UseConfigurablePool::kNo,
  58. });
  59. root.UncapEmptySlotSpanMemoryForTesting();
  60. const size_t kAllocSize = 100;
  61. void* data = root.Alloc(kAllocSize, "");
  62. void* data2 = root.Alloc(kAllocSize, "");
  63. root.Free(data2);
  64. root.Free(data);
  65. // See "PartialCorruption" above for details. This time, make shadow_
  66. // consistent.
  67. PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
  68. to_corrupt, true);
  69. // Crashes, because |to_corrupt| is not on the same superpage as data.
  70. EXPECT_DEATH(root.Alloc(kAllocSize, ""), "");
  71. }
  72. TEST(HardeningTest, MetadataPointerCrashing) {
  73. PartitionRoot<ThreadSafe> root({
  74. PartitionOptions::AlignedAlloc::kAllowed,
  75. PartitionOptions::ThreadCache::kDisabled,
  76. PartitionOptions::Quarantine::kDisallowed,
  77. PartitionOptions::Cookie::kDisallowed,
  78. PartitionOptions::BackupRefPtr::kDisabled,
  79. PartitionOptions::BackupRefPtrZapping::kDisabled,
  80. PartitionOptions::UseConfigurablePool::kNo,
  81. });
  82. root.UncapEmptySlotSpanMemoryForTesting();
  83. const size_t kAllocSize = 100;
  84. void* data = root.Alloc(kAllocSize, "");
  85. void* data2 = root.Alloc(kAllocSize, "");
  86. root.Free(data2);
  87. root.Free(data);
  88. uintptr_t slot_start = root.ObjectToSlotStart(data);
  89. auto* metadata = SlotSpanMetadata<ThreadSafe>::FromSlotStart(slot_start);
  90. PartitionFreelistEntry::EmplaceAndInitForTest(slot_start, metadata, true);
  91. // Crashes, because |metadata| points inside the metadata area.
  92. EXPECT_DEATH(root.Alloc(kAllocSize, ""), "");
  93. }
  94. #endif // !BUILDFLAG(IS_ANDROID) && defined(GTEST_HAS_DEATH_TEST) &&
  95. // defined(PA_HAS_FREELIST_SHADOW_ENTRY)
  96. TEST(HardeningTest, SuccessfulCorruption) {
  97. PartitionRoot<ThreadSafe> root({
  98. PartitionOptions::AlignedAlloc::kAllowed,
  99. PartitionOptions::ThreadCache::kDisabled,
  100. PartitionOptions::Quarantine::kDisallowed,
  101. PartitionOptions::Cookie::kDisallowed,
  102. PartitionOptions::BackupRefPtr::kDisabled,
  103. PartitionOptions::BackupRefPtrZapping::kDisabled,
  104. PartitionOptions::UseConfigurablePool::kNo,
  105. });
  106. root.UncapEmptySlotSpanMemoryForTesting();
  107. uintptr_t* zero_vector = reinterpret_cast<uintptr_t*>(
  108. root.AllocWithFlags(AllocFlags::kZeroFill, 100 * sizeof(uintptr_t), ""));
  109. ASSERT_TRUE(zero_vector);
  110. // Pointer to the middle of an existing allocation.
  111. uintptr_t* to_corrupt = zero_vector + 20;
  112. const size_t kAllocSize = 100;
  113. void* data = root.Alloc(kAllocSize, "");
  114. void* data2 = root.Alloc(kAllocSize, "");
  115. root.Free(data2);
  116. root.Free(data);
  117. PartitionFreelistEntry::EmplaceAndInitForTest(root.ObjectToSlotStart(data),
  118. to_corrupt, true);
  119. // Next allocation is what was in
  120. // root->bucket->active_slot_span_head->freelist_head, so not the corrupted
  121. // pointer.
  122. void* new_data = root.Alloc(kAllocSize, "");
  123. ASSERT_EQ(new_data, data);
  124. // Not crashing, because a zeroed area is a "valid" freelist entry.
  125. void* new_data2 = root.Alloc(kAllocSize, "");
  126. // Now we have a pointer to the middle of an existing allocation.
  127. EXPECT_EQ(new_data2, to_corrupt);
  128. }
  129. } // namespace
  130. } // namespace partition_alloc::internal
  131. #endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)