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 #include "base/allocator/partition_allocator/src/partition_alloc/partition_address_space.h"
7 #include "base/memory/safety_checks.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9
10 namespace {
11 using base::internal::is_memory_safety_checked;
12 using base::internal::MemorySafetyCheck;
13
14 // Normal object: should be targeted by no additional |MemorySafetyCheck|.
15 struct DefaultChecks {
16 public:
17 char data[16];
18 };
19
20 // Annotated object: should have |base::internal::kAdvancedMemorySafetyChecks|.
21 struct AdvancedChecks {
22 ADVANCED_MEMORY_SAFETY_CHECKS();
23
24 public:
25 char data[16];
26 };
27
28 // Annotated and aligned object for testing aligned allocations.
29 constexpr int kLargeAlignment = 2 * __STDCPP_DEFAULT_NEW_ALIGNMENT__;
30 struct alignas(kLargeAlignment) AlignedAdvancedChecks {
31 ADVANCED_MEMORY_SAFETY_CHECKS();
32
33 public:
34 char data[16];
35 };
36
37 // The macro may hook memory allocation/deallocation but should forward the
38 // request to PA or any other allocator via
39 // |HandleMemorySafetyCheckedOperator***|.
TEST(MemorySafetyCheckTest,AllocatorFunctions)40 TEST(MemorySafetyCheckTest, AllocatorFunctions) {
41 static_assert(
42 !is_memory_safety_checked<DefaultChecks,
43 MemorySafetyCheck::kForcePartitionAlloc>);
44 static_assert(
45 is_memory_safety_checked<AdvancedChecks,
46 MemorySafetyCheck::kForcePartitionAlloc>);
47 static_assert(
48 is_memory_safety_checked<AlignedAdvancedChecks,
49 MemorySafetyCheck::kForcePartitionAlloc>);
50
51 // void* operator new(std::size_t count);
52 auto* ptr1 = new DefaultChecks();
53 auto* ptr2 = new AdvancedChecks();
54 EXPECT_NE(ptr1, nullptr);
55 EXPECT_NE(ptr2, nullptr);
56
57 // AdvancedChecks is kForcePartitionAlloc.
58 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
59 EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
60 reinterpret_cast<uintptr_t>(ptr2)));
61 #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
62
63 // void operator delete(void* ptr);
64 delete ptr1;
65 delete ptr2;
66
67 // void* operator new(std::size_t count, std::align_val_t alignment)
68 ptr1 = new (std::align_val_t(64)) DefaultChecks();
69 ptr2 = new (std::align_val_t(64)) AdvancedChecks();
70 EXPECT_NE(ptr1, nullptr);
71 EXPECT_NE(ptr2, nullptr);
72
73 // AdvancedChecks is kForcePartitionAlloc.
74 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
75 EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
76 reinterpret_cast<uintptr_t>(ptr2)));
77 #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
78
79 // void operator delete(void* ptr, std::align_val_t alignment)
80 ::operator delete(ptr1, std::align_val_t(64));
81 AdvancedChecks::operator delete(ptr2, std::align_val_t(64));
82
83 // void* operator new(std::size_t count, std::align_val_t alignment)
84 auto* ptr3 = new AlignedAdvancedChecks();
85 EXPECT_NE(ptr3, nullptr);
86
87 // AlignedAdvancedChecks is kForcePartitionAlloc.
88 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
89 EXPECT_TRUE(partition_alloc::IsManagedByPartitionAlloc(
90 reinterpret_cast<uintptr_t>(ptr3)));
91 #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
92
93 // void operator delete(void* ptr, std::align_val_t alignment)
94 delete ptr3;
95
96 // void* operator new(std::size_t, void* ptr)
97 alignas(AlignedAdvancedChecks) char data[32];
98 ptr1 = new (data) DefaultChecks();
99 ptr2 = new (data) AdvancedChecks();
100 ptr3 = new (data) AlignedAdvancedChecks();
101 }
102
103 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
104
TEST(MemorySafetyCheckTest,SchedulerLoopQuarantine)105 TEST(MemorySafetyCheckTest, SchedulerLoopQuarantine) {
106 static_assert(
107 !is_memory_safety_checked<DefaultChecks,
108 MemorySafetyCheck::kSchedulerLoopQuarantine>);
109 static_assert(
110 is_memory_safety_checked<AdvancedChecks,
111 MemorySafetyCheck::kSchedulerLoopQuarantine>);
112
113 constexpr size_t kCapacityInBytes = 1024;
114
115 auto* root =
116 base::internal::GetPartitionRootForMemorySafetyCheckedAllocation();
117 auto& branch = root->GetSchedulerLoopQuarantineBranchForTesting();
118
119 size_t original_capacity_in_bytes = branch.GetRoot().GetCapacityInBytes();
120 branch.GetRoot().SetCapacityInBytesForTesting(kCapacityInBytes);
121
122 auto* ptr1 = new DefaultChecks();
123 EXPECT_NE(ptr1, nullptr);
124 delete ptr1;
125 EXPECT_FALSE(branch.IsQuarantinedForTesting(ptr1));
126
127 auto* ptr2 = new AdvancedChecks();
128 EXPECT_NE(ptr2, nullptr);
129 delete ptr2;
130 EXPECT_TRUE(branch.IsQuarantinedForTesting(ptr2));
131
132 branch.Purge();
133 branch.GetRoot().SetCapacityInBytesForTesting(original_capacity_in_bytes);
134 }
135
TEST(MemorySafetyCheckTest,ZapOnFree)136 TEST(MemorySafetyCheckTest, ZapOnFree) {
137 static_assert(
138 !is_memory_safety_checked<DefaultChecks, MemorySafetyCheck::kZapOnFree>);
139 static_assert(
140 is_memory_safety_checked<AdvancedChecks, MemorySafetyCheck::kZapOnFree>);
141
142 {
143 // Without kZapOnFree.
144 auto* ptr = new DefaultChecks();
145 EXPECT_NE(ptr, nullptr);
146 delete ptr;
147 // *ptr is undefined.
148 }
149
150 {
151 // With kZapOnFree.
152 auto* ptr = new AdvancedChecks();
153 EXPECT_NE(ptr, nullptr);
154 memset(ptr->data, 'A', sizeof(ptr->data));
155 delete ptr;
156
157 // Dereferencing `ptr` is still undefiner behavior, but we can say it is
158 // somewhat defined as this test is gated behind
159 // `BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)`.
160 // I believe behavior here is concrete enough to be tested, but it can be
161 // affected by changes in PA. Please disable this test if it flakes.
162 EXPECT_NE(ptr->data[0], 'A');
163 }
164 }
165
166 #endif // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
167
168 } // namespace
169