• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/buildflags.h"
18 #include "partition_alloc/dangling_raw_ptr_checks.h"
19 #include "partition_alloc/partition_alloc_base/cpu.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 PA_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 PA_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("[DanglingSignature]\t"),
87             HasSubstr("[DanglingPtr](1/3) A raw_ptr/raw_ref is dangling."),
88             HasSubstr("[DanglingPtr](2/3) First, the memory was freed at:"),
89             HasSubstr("[DanglingPtr](3/3) Later, the dangling raw_ptr was "
90                       "released at:")));
91 }
92 
93 // The StackTrace buffer might run out of storage and not record where the
94 // memory was freed. Anyway, it must still report the error.
TEST(PartitionAllocDanglingPtrChecks,FreeNotRecorded)95 TEST(PartitionAllocDanglingPtrChecks, FreeNotRecorded) {
96   EXPECT_DEATH(
97       {
98         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks;
99         partition_alloc::GetDanglingRawPtrReleasedFn()(42);
100       },
101       AllOf(HasSubstr("[DanglingSignature]\tmissing\tmissing\t"),
102             HasSubstr("[DanglingPtr](1/3) A raw_ptr/raw_ref is dangling."),
103             HasSubstr("[DanglingPtr](2/3) It was not recorded where the memory "
104                       "was freed."),
105             HasSubstr("[DanglingPtr](3/3) Later, the dangling raw_ptr was "
106                       "released at:")));
107 }
108 
109 // TODO(crbug.com/40260713): Check for leaked refcount on Android.
110 #if BUILDFLAG(IS_ANDROID)
111 // Some raw_ptr might never release their refcount. Make sure this cause a
112 // crash on exit.
TEST(PartitionAllocDanglingPtrChecks,ReleaseNotRecorded)113 TEST(PartitionAllocDanglingPtrChecks, ReleaseNotRecorded) {
114   EXPECT_DEATH(
115       {
116         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks;
117         partition_alloc::GetDanglingRawPtrDetectedFn()(42);
118       },
119       HasSubstr("A freed allocation is still referenced by a dangling pointer "
120                 "at exit, or at test end. Leaked raw_ptr/raw_ref "
121                 "could cause PartitionAlloc's quarantine memory bloat."
122                 "\n\n"
123                 "Memory was released on:"));
124 }
125 #endif
126 
127 // Getting the same allocation reported twice in a row, without matching
128 // `DanglingRawPtrReleased` in between is unexpected. Make sure this kind of
129 // potential regression would be detected.
TEST(PartitionAllocDanglingPtrChecks,DoubleDetection)130 TEST(PartitionAllocDanglingPtrChecks, DoubleDetection) {
131   EXPECT_DCHECK_DEATH_WITH(
132       {
133         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks;
134         partition_alloc::GetDanglingRawPtrDetectedFn()(42);
135         partition_alloc::GetDanglingRawPtrDetectedFn()(42);
136       },
137       "Check failed: !entry \\|\\| entry->id != id");
138 }
139 
140 // Free and release from two different tasks with cross task dangling pointer
141 // detection enabled.
TEST(PartitionAllocDanglingPtrChecks,CrossTask)142 TEST(PartitionAllocDanglingPtrChecks, CrossTask) {
143   BASE_EXPECT_DEATH(
144       {
145         ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
146             .type = "cross_task",
147         });
148 
149         base::test::TaskEnvironment task_environment;
150         task_environment.GetMainThreadTaskRunner()->PostTask(
151             FROM_HERE,
152             base::BindOnce(partition_alloc::GetDanglingRawPtrDetectedFn(), 42));
153         task_environment.GetMainThreadTaskRunner()->PostTask(
154             FROM_HERE,
155             base::BindOnce(partition_alloc::GetDanglingRawPtrReleasedFn(), 42));
156 
157         task_environment.RunUntilIdle();
158       },
159       AllOf(HasSubstr("[DanglingSignature]\t"),
160             HasSubstr("[DanglingPtr](1/3) A raw_ptr/raw_ref is dangling."),
161             HasSubstr("[DanglingPtr](2/3) First, the memory was freed at:"),
162             HasSubstr("[DanglingPtr](3/3) Later, the dangling raw_ptr was "
163                       "released at:")));
164 }
165 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskIgnoredFailuresClearsCache)166 TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoredFailuresClearsCache) {
167   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
168       .type = "cross_task",
169   });
170 
171   base::test::TaskEnvironment task_environment;
172   partition_alloc::GetDanglingRawPtrDetectedFn()(42);
173   partition_alloc::GetDanglingRawPtrReleasedFn()(42);
174   task_environment.GetMainThreadTaskRunner()->PostTask(
175       FROM_HERE,
176       base::BindOnce(partition_alloc::GetDanglingRawPtrReleasedFn(), 42));
177   task_environment.RunUntilIdle();
178 }
179 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskIgnoresNoTask)180 TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresNoTask) {
181   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
182       .type = "cross_task",
183   });
184 
185   partition_alloc::GetDanglingRawPtrDetectedFn()(42);
186   partition_alloc::GetDanglingRawPtrReleasedFn()(42);
187 }
188 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskIgnoresSameTask)189 TEST(PartitionAllocDanglingPtrChecks, CrossTaskIgnoresSameTask) {
190   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
191       .type = "cross_task",
192   });
193 
194   base::test::TaskEnvironment task_environment;
195   task_environment.GetMainThreadTaskRunner()->PostTask(
196       FROM_HERE, base::BindOnce([] {
197         partition_alloc::GetDanglingRawPtrDetectedFn()(37);
198         partition_alloc::GetDanglingRawPtrReleasedFn()(37);
199       }));
200   task_environment.RunUntilIdle();
201 }
202 
TEST(PartitionAllocDanglingPtrChecks,CrossTaskNoFreeConsideredCrossTask)203 TEST(PartitionAllocDanglingPtrChecks, CrossTaskNoFreeConsideredCrossTask) {
204   ScopedInstallDanglingRawPtrChecks scoped_install_dangling_checks({
205       .type = "cross_task",
206   });
207   partition_alloc::GetDanglingRawPtrReleasedFn()(42);
208 }
209 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureMacStackTrace)210 TEST(PartitionAllocDanglingPtrChecks,
211      ExtractDanglingPtrSignatureMacStackTrace) {
212   const std::string stack_trace_output =
213       "0   lib_1  0x0000000115fdfa12 base::F1(**) + 18\r\n"
214       "1   lib_1  0x0000000115ec0043 base::F2() + 19\r\n"
215       "2   lib_1  0x000000011601fb01 "
216       "allocator_shim::internal::PartitionFree(foo) + 13265\r\n"
217       "3   lib_1  0x0000000114831027 base::F3(bar) + 42\r\n"
218       "4   lib_2  0x00000001148eae35 base::F4() + 437\r\n";
219   EXPECT_EQ("base::F3(bar)",
220             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
221                 stack_trace_output));
222 }
223 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureMacTaskTrace)224 TEST(PartitionAllocDanglingPtrChecks, ExtractDanglingPtrSignatureMacTaskTrace) {
225   const std::string task_trace_output =
226       "Task trace:\r\n"
227       "0   lib_1  0x00000001161fd431 base::F1() + 257\r\n"
228       "1   lib_1  0x0000000115a49404 base::F2() + 68\r\n";
229   EXPECT_EQ("base::F1()",
230             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
231                 task_trace_output));
232 }
233 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureWindowsStackTrace)234 TEST(PartitionAllocDanglingPtrChecks,
235      ExtractDanglingPtrSignatureWindowsStackTrace) {
236   const std::string stack_trace_output =
237       "\tbase::F1 [0x055643C3+19] (o:\\base\\F1.cc:329)\r\n"
238       "\tallocator_shim::internal::PartitionFree [0x0648F87B+5243] "
239       "(o:\\path.cc:441)\r\n"
240       "\t_free_base [0x0558475D+29] (o:\\file_path.cc:142)\r\n"
241       "\tbase::F2 [0x04E5B317+23] (o:\\base\\F2.cc:91)\r\n"
242       "\tbase::F3 [0x04897800+544] (o:\\base\\F3.cc:638)\r\n";
243   EXPECT_EQ("base::F2",
244             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
245                 stack_trace_output));
246 }
247 
TEST(PartitionAllocDanglingPtrChecks,ExtractDanglingPtrSignatureWindowsTaskTrace)248 TEST(PartitionAllocDanglingPtrChecks,
249      ExtractDanglingPtrSignatureWindowsTaskTrace) {
250   const std::string task_trace_output =
251       "Task trace:\r\n"
252       "\tbase::F1 [0x049068A3+813] (o:\\base\\F1.cc:207)\r\n"
253       "\tbase::F2 [0x0490614C+192] (o:\\base\\F2.cc:116)\r\n";
254   EXPECT_EQ("base::F1",
255             PartitionAllocSupport::ExtractDanglingPtrSignatureForTests(
256                 task_trace_output));
257 }
258 
259 #endif
260 
261 #if PA_BUILDFLAG(HAS_MEMORY_TAGGING)
TEST(PartitionAllocSupportTest,ProposeSyntheticFinchTrials_MemoryTaggingDogfood)262 TEST(PartitionAllocSupportTest,
263      ProposeSyntheticFinchTrials_MemoryTaggingDogfood) {
264   {
265     test::ScopedFeatureList scope;
266     scope.InitWithFeatures({}, {features::kPartitionAllocMemoryTagging});
267 
268     auto trials = ProposeSyntheticFinchTrials();
269 
270     auto group_iter = trials.find("MemoryTaggingDogfood");
271     EXPECT_EQ(group_iter, trials.end());
272   }
273 
274   {
275     test::ScopedFeatureList scope;
276     scope.InitWithFeatures({features::kPartitionAllocMemoryTagging}, {});
277 
278     auto trials = ProposeSyntheticFinchTrials();
279 
280     std::string expectation =
281         partition_alloc::internal::base::CPU::GetInstanceNoAllocation()
282                 .has_mte()
283             ? "Enabled"
284             : "Disabled";
285     auto group_iter = trials.find("MemoryTaggingDogfood");
286     EXPECT_NE(group_iter, trials.end());
287     EXPECT_EQ(group_iter->second, expectation);
288   }
289 }
290 #endif  // PA_BUILDFLAG(HAS_MEMORY_TAGGING)
291 
292 class MemoryReclaimerSupportTest : public ::testing::Test {
293  public:
SetUp()294   void SetUp() override {
295     feature_list_.InitWithFeatures(
296         {base::features::kPartitionAllocMemoryReclaimer,
297          base::allocator::kDisableMemoryReclaimerInBackground},
298         {});
299     MemoryReclaimerSupport::Instance().ResetForTesting();
300   }
301 
302  protected:
303   base::test::SingleThreadTaskEnvironment task_environment_{
304       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
305   test::ScopedFeatureList feature_list_;
306 };
307 
TEST_F(MemoryReclaimerSupportTest,StartSeveralTimes)308 TEST_F(MemoryReclaimerSupportTest, StartSeveralTimes) {
309   test::ScopedFeatureList feature_list{
310       base::features::kPartitionAllocMemoryReclaimer};
311   auto& instance = MemoryReclaimerSupport::Instance();
312   EXPECT_FALSE(instance.has_pending_task_for_testing());
313   instance.Start(task_environment_.GetMainThreadTaskRunner());
314   instance.Start(task_environment_.GetMainThreadTaskRunner());
315   instance.Start(task_environment_.GetMainThreadTaskRunner());
316   // Only one task.
317   EXPECT_TRUE(instance.has_pending_task_for_testing());
318   EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount());
319 }
320 
TEST_F(MemoryReclaimerSupportTest,ForegroundToBackground)321 TEST_F(MemoryReclaimerSupportTest, ForegroundToBackground) {
322   test::ScopedFeatureList feature_list{
323       base::features::kPartitionAllocMemoryReclaimer};
324   auto& instance = MemoryReclaimerSupport::Instance();
325   EXPECT_FALSE(instance.has_pending_task_for_testing());
326   instance.Start(task_environment_.GetMainThreadTaskRunner());
327   EXPECT_TRUE(instance.has_pending_task_for_testing());
328   EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount());
329 
330   task_environment_.FastForwardBy(
331       MemoryReclaimerSupport::kFirstPAPurgeOrReclaimDelay);
332   // Task gets reposted.
333   EXPECT_TRUE(instance.has_pending_task_for_testing());
334   EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount());
335 
336   instance.SetForegrounded(false);
337   task_environment_.FastForwardBy(MemoryReclaimerSupport::GetInterval());
338   // But not once in background.
339   EXPECT_FALSE(instance.has_pending_task_for_testing());
340   EXPECT_EQ(0u, task_environment_.GetPendingMainThreadTaskCount());
341 }
342 
TEST_F(MemoryReclaimerSupportTest,ForegroundToBackgroundAndBack)343 TEST_F(MemoryReclaimerSupportTest, ForegroundToBackgroundAndBack) {
344   test::ScopedFeatureList feature_list{
345       base::features::kPartitionAllocMemoryReclaimer};
346   auto& instance = MemoryReclaimerSupport::Instance();
347   instance.Start(task_environment_.GetMainThreadTaskRunner());
348   task_environment_.FastForwardBy(
349       MemoryReclaimerSupport::kFirstPAPurgeOrReclaimDelay);
350 
351   // Task gets reposted.
352   EXPECT_TRUE(instance.has_pending_task_for_testing());
353   EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount());
354 
355   instance.SetForegrounded(false);
356   task_environment_.FastForwardBy(MemoryReclaimerSupport::GetInterval());
357   // But not once in background.
358   EXPECT_FALSE(instance.has_pending_task_for_testing());
359   EXPECT_EQ(0u, task_environment_.GetPendingMainThreadTaskCount());
360 
361   // Until we go to foreground again.
362   instance.SetForegrounded(true);
363   EXPECT_TRUE(instance.has_pending_task_for_testing());
364   EXPECT_EQ(1u, task_environment_.GetPendingMainThreadTaskCount());
365 }
366 
367 }  // namespace base::allocator
368