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