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