xref: /aosp_15_r20/external/cronet/base/memory/safety_checks_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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