• 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 <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