• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/memory/safety_checks.h"
6 
7 #include <new>
8 
9 #include "base/allocator/partition_alloc_features.h"
10 #include "base/feature_list.h"
11 #include "partition_alloc/partition_address_space.h"
12 #include "partition_alloc/tagging.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 namespace {
16 using base::internal::is_memory_safety_checked;
17 using base::internal::MemorySafetyCheck;
18 
19 // Normal object: should be targeted by no additional |MemorySafetyCheck|.
20 struct DefaultChecks {
21  public:
22   char data[16];
23 };
24 
25 // Annotated object: should have |base::internal::kAdvancedMemorySafetyChecks|.
26 struct AdvancedChecks {
27   ADVANCED_MEMORY_SAFETY_CHECKS();
28 
29  public:
30   char data[16];
31 };
32 
33 // Annotated object: should have |base::internal::kAdvancedMemorySafetyChecks|.
34 struct AnotherAdvancedChecks {
35   ADVANCED_MEMORY_SAFETY_CHECKS();
36 
37  public:
38   char data[16];
39 };
40 
41 // Annotated and aligned object for testing aligned allocations.
42 constexpr int kLargeAlignment = 2 * __STDCPP_DEFAULT_NEW_ALIGNMENT__;
43 struct alignas(kLargeAlignment) AlignedAdvancedChecks {
44   ADVANCED_MEMORY_SAFETY_CHECKS();
45 
46  public:
47   char data[16];
48 };
49 
50 struct PrivateInheritanceWithInheritMacro : private AdvancedChecks {
51   INHERIT_MEMORY_SAFETY_CHECKS(AdvancedChecks);
52 };
53 static_assert(
54     is_memory_safety_checked<PrivateInheritanceWithInheritMacro,
55                              MemorySafetyCheck::kForcePartitionAlloc>);
56 
57 struct PrivateInheritanceWithDefaultMacro : private AdvancedChecks {
58   DEFAULT_MEMORY_SAFETY_CHECKS();
59 };
60 static_assert(
61     !is_memory_safety_checked<PrivateInheritanceWithDefaultMacro,
62                               MemorySafetyCheck::kForcePartitionAlloc>);
63 
64 struct MultipleInheritanceWithInheritMacro : AdvancedChecks,
65                                              AnotherAdvancedChecks {
66   INHERIT_MEMORY_SAFETY_CHECKS(AdvancedChecks);
67 };
68 static_assert(
69     is_memory_safety_checked<MultipleInheritanceWithInheritMacro,
70                              MemorySafetyCheck::kForcePartitionAlloc>);
71 
72 struct MultipleInheritanceWithDefaultMacro : AdvancedChecks,
73                                              AnotherAdvancedChecks {
74   DEFAULT_MEMORY_SAFETY_CHECKS();
75 };
76 static_assert(
77     !is_memory_safety_checked<MultipleInheritanceWithDefaultMacro,
78                               MemorySafetyCheck::kForcePartitionAlloc>);
79 
80 struct AdvancedChecksWithPartialOverwrite {
81   ADVANCED_MEMORY_SAFETY_CHECKS(kNone, kForcePartitionAlloc);
82 
83  public:
84   char data[16];
85 };
86 static_assert(
87     !is_memory_safety_checked<AdvancedChecksWithPartialOverwrite,
88                               MemorySafetyCheck::kForcePartitionAlloc>);
89 
90 struct InheritanceWithPartialOverwrite : private AdvancedChecks {
91   INHERIT_MEMORY_SAFETY_CHECKS(AdvancedChecks, kNone, kForcePartitionAlloc);
92 };
93 static_assert(
94     !is_memory_safety_checked<InheritanceWithPartialOverwrite,
95                               MemorySafetyCheck::kForcePartitionAlloc>);
96 
97 // The macro may hook memory allocation/deallocation but should forward the
98 // request to PA or any other allocator via
99 // |HandleMemorySafetyCheckedOperator***|.
TEST(MemorySafetyCheckTest,AllocatorFunctions)100 TEST(MemorySafetyCheckTest, AllocatorFunctions) {
101   static_assert(
102       !is_memory_safety_checked<DefaultChecks,
103                                 MemorySafetyCheck::kForcePartitionAlloc>);
104   static_assert(
105       is_memory_safety_checked<AdvancedChecks,
106                                MemorySafetyCheck::kForcePartitionAlloc>);
107   static_assert(
108       is_memory_safety_checked<AlignedAdvancedChecks,
109                                MemorySafetyCheck::kForcePartitionAlloc>);
110 
111   // void* operator new(std::size_t count);
112   auto* ptr1 = new DefaultChecks();
113   auto* ptr2 = new AdvancedChecks();
114   EXPECT_NE(ptr1, nullptr);
115   EXPECT_NE(ptr2, nullptr);
116 
117 // AdvancedChecks is kForcePartitionAlloc.
118 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
119   EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
120       reinterpret_cast<uintptr_t>(partition_alloc::UntagPtr(ptr2))));
121 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
122 
123   // void operator delete(void* ptr);
124   delete ptr1;
125   delete ptr2;
126 
127   // void* operator new(std::size_t count, std::align_val_t alignment)
128   ptr1 = new (std::align_val_t(64)) DefaultChecks();
129   ptr2 = new (std::align_val_t(64)) AdvancedChecks();
130   EXPECT_NE(ptr1, nullptr);
131   EXPECT_NE(ptr2, nullptr);
132 
133 // AdvancedChecks is kForcePartitionAlloc.
134 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
135   EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
136       reinterpret_cast<uintptr_t>(partition_alloc::UntagPtr(ptr2))));
137 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
138 
139   // void operator delete(void* ptr, std::align_val_t alignment)
140   ::operator delete(ptr1, std::align_val_t(64));
141   AdvancedChecks::operator delete(ptr2, std::align_val_t(64));
142 
143   // void* operator new(std::size_t count, std::align_val_t alignment)
144   auto* ptr3 = new AlignedAdvancedChecks();
145   EXPECT_NE(ptr3, nullptr);
146 
147 // AlignedAdvancedChecks is kForcePartitionAlloc.
148 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
149   EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
150       reinterpret_cast<uintptr_t>(partition_alloc::UntagPtr(ptr3))));
151 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
152 
153   // void operator delete(void* ptr, std::align_val_t alignment)
154   delete ptr3;
155 
156   // void* operator new(std::size_t, void* ptr)
157   alignas(AlignedAdvancedChecks) char data[32];
158   ptr1 = new (data) DefaultChecks();
159   ptr2 = new (data) AdvancedChecks();
160   ptr3 = new (data) AlignedAdvancedChecks();
161 }
162 
163 #if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
164 
TEST(MemorySafetyCheckTest,SchedulerLoopQuarantine)165 TEST(MemorySafetyCheckTest, SchedulerLoopQuarantine) {
166   // The check is performed only if `kPartitionAllocSchedulerLoopQuarantine` is
167   // enabled. `base::ScopedFeatureList` does not work here because the default
168   // `PartitionRoot` is configured before running this test.
169   if (!base::FeatureList::IsEnabled(
170           base::features::kPartitionAllocSchedulerLoopQuarantine)) {
171     return;
172   }
173 
174   static_assert(
175       !is_memory_safety_checked<DefaultChecks,
176                                 MemorySafetyCheck::kSchedulerLoopQuarantine>);
177   static_assert(
178       is_memory_safety_checked<AdvancedChecks,
179                                MemorySafetyCheck::kSchedulerLoopQuarantine>);
180 
181   auto* root =
182       base::internal::GetPartitionRootForMemorySafetyCheckedAllocation();
183   auto& branch = root->GetSchedulerLoopQuarantineBranchForTesting();
184 
185   auto* ptr1 = new DefaultChecks();
186   EXPECT_NE(ptr1, nullptr);
187   delete ptr1;
188   EXPECT_FALSE(branch.IsQuarantinedForTesting(ptr1));
189 
190   auto* ptr2 = new AdvancedChecks();
191   EXPECT_NE(ptr2, nullptr);
192   delete ptr2;
193   EXPECT_TRUE(branch.IsQuarantinedForTesting(ptr2));
194 
195   branch.Purge();
196 }
197 
TEST(MemorySafetyCheckTest,ZapOnFree)198 TEST(MemorySafetyCheckTest, ZapOnFree) {
199   // The check is performed only if `kPartitionAllocZappingByFreeFlags` is
200   // enabled. `base::ScopedFeatureList` does not work here because the default
201   // `PartitionRoot` is configured before running this test.
202   if (!base::FeatureList::IsEnabled(
203           base::features::kPartitionAllocZappingByFreeFlags)) {
204     return;
205   }
206 
207   static_assert(
208       !is_memory_safety_checked<DefaultChecks, MemorySafetyCheck::kZapOnFree>);
209   static_assert(
210       is_memory_safety_checked<AdvancedChecks, MemorySafetyCheck::kZapOnFree>);
211 
212   {
213     // Without kZapOnFree.
214     auto* ptr = new DefaultChecks();
215     EXPECT_NE(ptr, nullptr);
216     delete ptr;
217     // *ptr is undefined.
218   }
219 
220   {
221     // With kZapOnFree.
222     auto* ptr = new AdvancedChecks();
223     EXPECT_NE(ptr, nullptr);
224     memset(ptr->data, 'A', sizeof(ptr->data));
225     delete ptr;
226 
227     // Dereferencing `ptr` is still undefiner behavior, but we can say it is
228     // somewhat defined as this test is gated behind
229     // `PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)`.
230     // I believe behavior here is concrete enough to be tested, but it can be
231     // affected by changes in PA. Please disable this test if it flakes.
232     EXPECT_NE(ptr->data[0], 'A');
233   }
234 }
235 
236 #endif  // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
237 
238 }  // namespace
239