xref: /aosp_15_r20/external/cronet/base/allocator/partition_alloc_support_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2021 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/allocator/partition_alloc_support.h"
6 
7 #include <array>
8 #include <string>
9 #include <utility>
10 #include <vector>
11 
12 #include "base/allocator/partition_alloc_features.h"
13 #include "base/test/gtest_util.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "base/test/task_environment.h"
16 #include "build/build_config.h"
17 #include "partition_alloc/dangling_raw_ptr_checks.h"
18 #include "partition_alloc/partition_alloc_base/cpu.h"
19 #include "partition_alloc/partition_alloc_buildflags.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 namespace base::allocator {
24 
25 using testing::AllOf;
26 using testing::HasSubstr;
27 
TEST(PartitionAllocSupportTest,ProposeSyntheticFinchTrials_DanglingPointerDetector)28 TEST(PartitionAllocSupportTest,
29      ProposeSyntheticFinchTrials_DanglingPointerDetector) {
30   std::string dpd_group =
31       ProposeSyntheticFinchTrials()["DanglingPointerDetector"];
32 
33 #if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS)
34   EXPECT_EQ(dpd_group, "Enabled");
35 #else
36   EXPECT_EQ(dpd_group, "Disabled");
37 #endif
38 }
39 
40 // - Death tests misbehave on Android, http://crbug.com/643760.
41 #if BUILDFLAG(ENABLE_DANGLING_RAW_PTR_CHECKS) && !BUILDFLAG(IS_ANDROID) && \
42     defined(GTEST_HAS_DEATH_TEST)
43 
44 namespace {
45 
46 // Install dangling raw_ptr handler and restore them when going out of scope.
47 class ScopedInstallDanglingRawPtrChecks {
48  public:
49   struct ConstructorParams {
50     std::string mode = "crash";
51     std::string type = "all";
52   };
ScopedInstallDanglingRawPtrChecks(ConstructorParams params)53   ScopedInstallDanglingRawPtrChecks(ConstructorParams params) {
54     enabled_feature_list_.InitWithFeaturesAndParameters(
55         {{features::kPartitionAllocDanglingPtr,
56           {{"mode", params.mode}, {"type", params.type}}}},
57         {/* disabled_features */});
58 
59     old_detected_fn_ = partition_alloc::GetDanglingRawPtrDetectedFn();
60     old_dereferenced_fn_ = partition_alloc::GetDanglingRawPtrReleasedFn();
61     InstallDanglingRawPtrChecks();
62   }
ScopedInstallDanglingRawPtrChecks()63   ScopedInstallDanglingRawPtrChecks()
64       : ScopedInstallDanglingRawPtrChecks(ConstructorParams{}) {}
~ScopedInstallDanglingRawPtrChecks()65   ~ScopedInstallDanglingRawPtrChecks() {
66     InstallDanglingRawPtrChecks();  // Check for leaks.
67     partition_alloc::SetDanglingRawPtrDetectedFn(old_detected_fn_);
68     partition_alloc::SetDanglingRawPtrReleasedFn(old_dereferenced_fn_);
69   }
70 
71  private:
72   test::ScopedFeatureList enabled_feature_list_;
73   partition_alloc::DanglingRawPtrDetectedFn* old_detected_fn_;
74   partition_alloc::DanglingRawPtrReleasedFn* old_dereferenced_fn_;
75 };
76 
77 }  // namespace
78 
TEST(PartitionAllocDanglingPtrChecks,Basic)79 TEST(PartitionAllocDanglingPtrChecks, Basic) {
80   EXPECT_DEATH(
81       {
82         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks;
83         partition_alloc::GetDanglingRawPtrDetectedFn()(42);
84         partition_alloc::GetDanglingRawPtrReleasedFn()(42);
85       },
86       AllOf(HasSubstr("Detected dangling raw_ptr with id=0x000000000000002a:"),
87             HasSubstr("[DanglingSignature]\t"),
88             HasSubstr("The memory was freed at:"),
89             HasSubstr("The dangling raw_ptr was released at:")));
90 }
91 
92 // The StackTrace buffer might run out of storage and not record where the
93 // memory was freed. Anyway, it must still report the error.
TEST(PartitionAllocDanglingPtrChecks,FreeNotRecorded)94 TEST(PartitionAllocDanglingPtrChecks, FreeNotRecorded) {
95   EXPECT_DEATH(
96       {
97         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks;
98         partition_alloc::GetDanglingRawPtrReleasedFn()(42);
99       },
100       AllOf(HasSubstr("Detected dangling raw_ptr with id=0x000000000000002a:"),
101             HasSubstr("[DanglingSignature]\tmissing\tmissing\t"),
102             HasSubstr("It was not recorded where the memory was freed."),
103             HasSubstr("The dangling raw_ptr was released at:")));
104 }
105 
106 // TODO(https://crbug.com/1425095): Check for leaked refcount on Android.
107 #if BUILDFLAG(IS_ANDROID)
108 // Some raw_ptr might never release their refcount. Make sure this cause a
109 // crash on exit.
TEST(PartitionAllocDanglingPtrChecks,ReleaseNotRecorded)110 TEST(PartitionAllocDanglingPtrChecks, ReleaseNotRecorded) {
111   EXPECT_DEATH(
112       {
113         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks;
114         partition_alloc::GetDanglingRawPtrDetectedFn()(42);
115       },
116       HasSubstr("A freed allocation is still referenced by a dangling pointer "
117                 "at exit, or at test end. Leaked raw_ptr/raw_ref "
118                 "could cause PartitionAlloc's quarantine memory bloat."
119                 "\n\n"
120                 "Memory was released on:"));
121 }
122 #endif
123 
124 // Getting the same allocation reported twice in a row, without matching
125 // `DanglingRawPtrReleased` in between is unexpected. Make sure this kind of
126 // potential regression would be detected.
TEST(PartitionAllocDanglingPtrChecks,DoubleDetection)127 TEST(PartitionAllocDanglingPtrChecks, DoubleDetection) {
128   EXPECT_DCHECK_DEATH_WITH(
129       {
130         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks;
131         partition_alloc::GetDanglingRawPtrDetectedFn()(42);
132         partition_alloc::GetDanglingRawPtrDetectedFn()(42);
133       },
134       "Check failed: !entry \\|\\| entry->id != id");
135 }
136 
137 // Free and release from two different tasks with cross task dangling pointer
138 // detection enabled.
TEST(PartitionAllocDanglingPtrChecks,CrossTask)139 TEST(PartitionAllocDanglingPtrChecks, CrossTask) {
140   BASE_EXPECT_DEATH(
141       {
142         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
143             .type = "cross_task",
144         });
145 
146         base::test::TaskEnvironment task_environment;
147         task_environment.GetMainThreadTaskRunner()->PostTask(
148             FROM_HERE,
149             base::BindOnce(partition_alloc::GetDanglingRawPtrDetectedFn(), 42));
150         task_environment.GetMainThreadTaskRunner()->PostTask(
151             FROM_HERE,
152             base::BindOnce(partition_alloc::GetDanglingRawPtrReleasedFn(), 42));
153 
154         task_environment.RunUntilIdle();
155       },
156       AllOf(HasSubstr("Detected dangling raw_ptr with id=0x000000000000002a:"),
157             HasSubstr("[DanglingSignature]\t"),
158             HasSubstr("The memory was freed at:"),
159             HasSubstr("The dangling raw_ptr was released at:")));
160 }
161 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskIgnoredFailuresClearsCache)162 TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoredFailuresClearsCache) {
163   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
164       .type = "cross_task",
165   });
166 
167   base::test::TaskEnvironment task_environment;
168   partition_alloc::GetDanglingRawPtrDetectedFn()(42);
169   partition_alloc::GetDanglingRawPtrReleasedFn()(42);
170   task_environment.GetMainThreadTaskRunner()->PostTask(
171       FROM_HERE,
172       base::BindOnce(partition_alloc::GetDanglingRawPtrReleasedFn(), 42));
173   task_environment.RunUntilIdle();
174 }
175 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskIgnoresNoTask)176 TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresNoTask) {
177   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
178       .type = "cross_task",
179   });
180 
181   partition_alloc::GetDanglingRawPtrDetectedFn()(42);
182   partition_alloc::GetDanglingRawPtrReleasedFn()(42);
183 }
184 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskIgnoresSameTask)185 TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresSameTask) {
186   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
187       .type = "cross_task",
188   });
189 
190   base::test::TaskEnvironment task_environment;
191   task_environment.GetMainThreadTaskRunner()->PostTask(
192       FROM_HERE, base::BindOnce([]() {
193         partition_alloc::GetDanglingRawPtrDetectedFn()(37);
194         partition_alloc::GetDanglingRawPtrReleasedFn()(37);
195       }));
196   task_environment.RunUntilIdle();
197 }
198 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskNoFreeConsideredCrossTask)199 TEST(PartitionAllocDanglingPtrChecks, CrossTaskNoFreeConsideredCrossTask) {
200   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
201       .type = "cross_task",
202   });
203   partition_alloc::GetDanglingRawPtrReleasedFn()(42);
204 }
205 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureMacStackTrace)206 TEST(PartitionAllocDanglingPtrChecks,
207      ExtractDanglingPtrSignatureMacStackTrace) {
208   const std::string stack_trace_output =
209       "0   lib_1  0x0000000115fdfa12 base::F1(**) + 18\r\n"
210       "1   lib_1  0x0000000115ec0043 base::F2() + 19\r\n"
211       "2   lib_1  0x000000011601fb01 "
212       "allocator_shim::internal::PartitionFree(foo) + 13265\r\n"
213       "3   lib_1  0x0000000114831027 base::F3(bar) + 42\r\n"
214       "4   lib_2  0x00000001148eae35 base::F4() + 437\r\n";
215   EXPECT_EQ("base::F3(bar)",
216             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
217                 stack_trace_output));
218 }
219 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureMacTaskTrace)220 TEST(PartitionAllocDanglingPtrChecks, ExtractDanglingPtrSignatureMacTaskTrace) {
221   const std::string task_trace_output =
222       "Task trace:\r\n"
223       "0   lib_1  0x00000001161fd431 base::F1() + 257\r\n"
224       "1   lib_1  0x0000000115a49404 base::F2() + 68\r\n";
225   EXPECT_EQ("base::F1()",
226             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
227                 task_trace_output));
228 }
229 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureWindowsStackTrace)230 TEST(PartitionAllocDanglingPtrChecks,
231      ExtractDanglingPtrSignatureWindowsStackTrace) {
232   const std::string stack_trace_output =
233       "\tbase::F1 [0x055643C3+19] (o:\\base\\F1.cc:329)\r\n"
234       "\tallocator_shim::internal::PartitionFree [0x0648F87B+5243] "
235       "(o:\\path.cc:441)\r\n"
236       "\t_free_base [0x0558475D+29] (o:\\file_path.cc:142)\r\n"
237       "\tbase::F2 [0x04E5B317+23] (o:\\base\\F2.cc:91)\r\n"
238       "\tbase::F3 [0x04897800+544] (o:\\base\\F3.cc:638)\r\n";
239   EXPECT_EQ("base::F2",
240             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
241                 stack_trace_output));
242 }
243 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureWindowsTaskTrace)244 TEST(PartitionAllocDanglingPtrChecks,
245      ExtractDanglingPtrSignatureWindowsTaskTrace) {
246   const std::string task_trace_output =
247       "Task trace:\r\n"
248       "\tbase::F1 [0x049068A3+813] (o:\\base\\F1.cc:207)\r\n"
249       "\tbase::F2 [0x0490614C+192] (o:\\base\\F2.cc:116)\r\n";
250   EXPECT_EQ("base::F1",
251             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
252                 task_trace_output));
253 }
254 
255 #endif
256 
257 #if BUILDFLAG(HAS_MEMORY_TAGGING)
TEST(PartitionAllocSupportTest,ProposeSyntheticFinchTrials_MemoryTaggingDogfood)258 TEST(PartitionAllocSupportTest,
259      ProposeSyntheticFinchTrials_MemoryTaggingDogfood) {
260   {
261     test::ScopedFeatureList scope;
262     scope.InitWithFeatures({}, {features::kPartitionAllocMemoryTagging});
263 
264     auto trials = ProposeSyntheticFinchTrials();
265 
266     auto group_iter = trials.find("MemoryTaggingDogfood");
267     EXPECT_EQ(group_iter, trials.end());
268   }
269 
270   {
271     test::ScopedFeatureList scope;
272     scope.InitWithFeatures({features::kPartitionAllocMemoryTagging}, {});
273 
274     auto trials = ProposeSyntheticFinchTrials();
275 
276     std::string expectation =
277         partition_alloc::internal::base::CPU::GetInstanceNoAllocation()
278                 .has_mte()
279             ? "Enabled"
280             : "Disabled";
281     auto group_iter = trials.find("MemoryTaggingDogfood");
282     EXPECT_NE(group_iter, trials.end());
283     EXPECT_EQ(group_iter->second, expectation);
284   }
285 }
286 #endif  // BUILDFLAG(HAS_MEMORY_TAGGING)
287 
288 }  // namespace base::allocator
289