xref: /aosp_15_r20/external/cronet/base/memory/raw_ptr_asan_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 "partition_alloc/partition_alloc_buildflags.h"
6 
7 #if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
8 
9 #include <sanitizer/asan_interface.h>
10 #include <thread>
11 
12 #include "base/debug/asan_service.h"
13 #include "base/functional/bind.h"
14 #include "base/functional/callback.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/memory/raw_ptr_asan_service.h"
17 #include "base/task/thread_pool.h"
18 #include "base/test/bind.h"
19 #include "base/test/task_environment.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 namespace base::internal {
24 
25 struct AsanStruct {
26   int x;
27 
funcbase::internal::AsanStruct28   void func() { ++x; }
29 };
30 
31 #define ASAN_BRP_PROTECTED(x) "MiraclePtr Status: PROTECTED\\n.*" x
32 #define ASAN_BRP_MANUAL_ANALYSIS(x) \
33   "MiraclePtr Status: MANUAL ANALYSIS REQUIRED\\n.*" x
34 #define ASAN_BRP_NOT_PROTECTED(x) "MiraclePtr Status: NOT PROTECTED\\n.*" x
35 
36 const char kAsanBrpProtected_Dereference[] =
37     ASAN_BRP_PROTECTED("dangling pointer was being dereferenced");
38 const char kAsanBrpProtected_Callback[] = ASAN_BRP_PROTECTED(
39     "crash occurred inside a callback where a raw_ptr<T> pointing to the same "
40     "region");
41 const char kAsanBrpMaybeProtected_Extraction[] = ASAN_BRP_MANUAL_ANALYSIS(
42     "pointer to the same region was extracted from a raw_ptr<T>");
43 const char kAsanBrpNotProtected_EarlyAllocation[] = ASAN_BRP_NOT_PROTECTED(
44     "crash occurred while accessing a region that was allocated before "
45     "MiraclePtr was activated");
46 const char kAsanBrpNotProtected_NoRawPtrAccess[] =
47     ASAN_BRP_NOT_PROTECTED("No raw_ptr<T> access to this region was detected");
48 const char kAsanBrpMaybeProtected_Race[] =
49     ASAN_BRP_MANUAL_ANALYSIS("\\nThe \"use\" and \"free\" threads don't match");
50 const char kAsanBrpMaybeProtected_ThreadPool[] =
51     ASAN_BRP_MANUAL_ANALYSIS("\\nThis crash occurred in the thread pool");
52 
53 // Instantiation failure message format is special.
54 const char kAsanBrp_Instantiation[] =
55     "crash occurred due to an attempt to assign a dangling pointer";
56 
57 #undef ASAN_BRP_PROTECTED
58 #undef ASAN_BRP_MANUAL_ANALYSIS
59 #undef ASAN_BRP_NOT_PROTECTED
60 
61 class AsanBackupRefPtrTest : public testing::Test {
62  protected:
SetUp()63   void SetUp() override {
64     base::debug::AsanService::GetInstance()->Initialize();
65 
66     if (!RawPtrAsanService::GetInstance().IsEnabled()) {
67       base::RawPtrAsanService::GetInstance().Configure(
68           base::EnableDereferenceCheck(true), base::EnableExtractionCheck(true),
69           base::EnableInstantiationCheck(true));
70     } else {
71       ASSERT_TRUE(base::RawPtrAsanService::GetInstance()
72                       .is_dereference_check_enabled());
73       ASSERT_TRUE(
74           base::RawPtrAsanService::GetInstance().is_extraction_check_enabled());
75       ASSERT_TRUE(base::RawPtrAsanService::GetInstance()
76                       .is_instantiation_check_enabled());
77     }
78   }
79 
SetUpTestSuite()80   static void SetUpTestSuite() { early_allocation_ptr_ = new AsanStruct; }
TearDownTestSuite()81   static void TearDownTestSuite() { delete early_allocation_ptr_; }
82   static raw_ptr<AsanStruct> early_allocation_ptr_;
83 };
84 
85 raw_ptr<AsanStruct> AsanBackupRefPtrTest::early_allocation_ptr_ = nullptr;
86 
TEST_F(AsanBackupRefPtrTest,Dereference)87 TEST_F(AsanBackupRefPtrTest, Dereference) {
88   raw_ptr<AsanStruct> protected_ptr = new AsanStruct;
89 
90   // The four statements below should succeed.
91   (*protected_ptr).x = 1;
92   (*protected_ptr).func();
93   ++(protected_ptr->x);
94   protected_ptr->func();
95 
96   delete protected_ptr.get();
97 
98   EXPECT_DEATH_IF_SUPPORTED((*protected_ptr).x = 1,
99                             kAsanBrpProtected_Dereference);
100   EXPECT_DEATH_IF_SUPPORTED((*protected_ptr).func(),
101                             kAsanBrpProtected_Dereference);
102   EXPECT_DEATH_IF_SUPPORTED(++(protected_ptr->x),
103                             kAsanBrpProtected_Dereference);
104   EXPECT_DEATH_IF_SUPPORTED(protected_ptr->func(),
105                             kAsanBrpProtected_Dereference);
106 
107   // The following statement should not trigger a dereference, so it should
108   // succeed without crashing even though *protected_ptr is no longer valid.
109   [[maybe_unused]] AsanStruct* ptr = protected_ptr;
110 }
111 
TEST_F(AsanBackupRefPtrTest,Extraction)112 TEST_F(AsanBackupRefPtrTest, Extraction) {
113   raw_ptr<AsanStruct> protected_ptr = new AsanStruct;
114 
115   AsanStruct* ptr1 = protected_ptr;  // Shouldn't crash.
116   ptr1->x = 0;
117 
118   delete protected_ptr.get();
119 
120   EXPECT_DEATH_IF_SUPPORTED(
121       {
122         AsanStruct* ptr2 = protected_ptr;
123         ptr2->x = 1;
124       },
125       kAsanBrpMaybeProtected_Extraction);
126 }
127 
TEST_F(AsanBackupRefPtrTest,Instantiation)128 TEST_F(AsanBackupRefPtrTest, Instantiation) {
129   AsanStruct* ptr = new AsanStruct;
130 
131   raw_ptr<AsanStruct> protected_ptr1 = ptr;  // Shouldn't crash.
132   protected_ptr1 = nullptr;
133 
134   delete ptr;
135 
136   EXPECT_DEATH_IF_SUPPORTED(
137       { [[maybe_unused]] raw_ptr<AsanStruct> protected_ptr2 = ptr; },
138       kAsanBrp_Instantiation);
139 }
140 
TEST_F(AsanBackupRefPtrTest,InstantiationInvalidPointer)141 TEST_F(AsanBackupRefPtrTest, InstantiationInvalidPointer) {
142   void* ptr1 = reinterpret_cast<void*>(0xfefefefefefefefe);
143 
144   [[maybe_unused]] raw_ptr<void> protected_ptr1 = ptr1;  // Shouldn't crash.
145 
146   size_t shadow_scale, shadow_offset;
147   __asan_get_shadow_mapping(&shadow_scale, &shadow_offset);
148   [[maybe_unused]] raw_ptr<void> protected_ptr2 =
149       reinterpret_cast<void*>(shadow_offset);  // Shouldn't crash.
150 }
151 
TEST_F(AsanBackupRefPtrTest,UserPoisoned)152 TEST_F(AsanBackupRefPtrTest, UserPoisoned) {
153   AsanStruct* ptr = new AsanStruct;
154   __asan_poison_memory_region(ptr, sizeof(AsanStruct));
155 
156   [[maybe_unused]] raw_ptr<AsanStruct> protected_ptr1 =
157       ptr;  // Shouldn't crash.
158 
159   delete ptr;  // Should crash now.
160   EXPECT_DEATH_IF_SUPPORTED(
161       { [[maybe_unused]] raw_ptr<AsanStruct> protected_ptr2 = ptr; },
162       kAsanBrp_Instantiation);
163 }
164 
TEST_F(AsanBackupRefPtrTest,EarlyAllocationDetection)165 TEST_F(AsanBackupRefPtrTest, EarlyAllocationDetection) {
166   raw_ptr<AsanStruct> late_allocation_ptr = new AsanStruct;
167   EXPECT_FALSE(RawPtrAsanService::GetInstance().IsSupportedAllocation(
168       early_allocation_ptr_.get()));
169   EXPECT_TRUE(RawPtrAsanService::GetInstance().IsSupportedAllocation(
170       late_allocation_ptr.get()));
171 
172   delete late_allocation_ptr.get();
173   delete early_allocation_ptr_.get();
174 
175   EXPECT_FALSE(RawPtrAsanService::GetInstance().IsSupportedAllocation(
176       early_allocation_ptr_.get()));
177   EXPECT_TRUE(RawPtrAsanService::GetInstance().IsSupportedAllocation(
178       late_allocation_ptr.get()));
179 
180   EXPECT_DEATH_IF_SUPPORTED({ early_allocation_ptr_->func(); },
181                             kAsanBrpNotProtected_EarlyAllocation);
182   EXPECT_DEATH_IF_SUPPORTED({ late_allocation_ptr->func(); },
183                             kAsanBrpProtected_Dereference);
184 
185   early_allocation_ptr_ = nullptr;
186 }
187 
TEST_F(AsanBackupRefPtrTest,BoundRawPtr)188 TEST_F(AsanBackupRefPtrTest, BoundRawPtr) {
189   // This test is for the handling of raw_ptr<T> type objects being passed
190   // directly to Bind.
191 
192   raw_ptr<AsanStruct> protected_ptr = new AsanStruct;
193 
194   // First create our test callbacks while `*protected_ptr` is still valid, and
195   // we will then invoke them after deleting `*protected_ptr`.
196 
197   // `ptr` is protected in this callback, as raw_ptr<T> will be mapped to an
198   // UnretainedWrapper containing a raw_ptr<T> which is guaranteed to outlive
199   // the function call.
200   auto ptr_callback = base::BindOnce(
201       [](AsanStruct* ptr) {
202         // This will crash and should be detected as a protected access.
203         ptr->func();
204       },
205       protected_ptr);
206 
207   // Now delete `*protected_ptr` and check that the callbacks we created are
208   // handled correctly.
209   delete protected_ptr.get();
210   protected_ptr = nullptr;
211 
212   EXPECT_DEATH_IF_SUPPORTED(std::move(ptr_callback).Run(),
213                             kAsanBrpProtected_Callback);
214 }
215 
TEST_F(AsanBackupRefPtrTest,BoundArgumentsProtected)216 TEST_F(AsanBackupRefPtrTest, BoundArgumentsProtected) {
217   raw_ptr<AsanStruct> protected_ptr = new AsanStruct;
218   raw_ptr<AsanStruct> protected_ptr2 = new AsanStruct;
219 
220   // First create our test callbacks while `*protected_ptr` is still valid, and
221   // we will then invoke them after deleting `*protected_ptr`.
222 
223   // `ptr` is protected in this callback even after `*ptr` has been deleted,
224   // since the allocation will be kept alive by the internal `raw_ptr<T>` inside
225   // base::Unretained().
226   auto safe_callback = base::BindOnce(
227       [](AsanStruct* ptr) {
228         // This will crash and should be detected as a protected access.
229         ptr->func();
230       },
231       base::Unretained(protected_ptr));
232 
233   // Both `inner_ptr` and `outer_ptr` are protected in these callbacks, since
234   // both are bound before `*ptr` is deleted. This test is making sure that
235   // `inner_ptr` is treated as protected.
236   auto safe_nested_inner_callback = base::BindOnce(
237       [](AsanStruct* outer_ptr, base::OnceClosure inner_callback) {
238         std::move(inner_callback).Run();
239         // This will never be executed, as we will crash in inner_callback
240         ASSERT_TRUE(false);
241       },
242       base::Unretained(protected_ptr),
243       base::BindOnce(
244           [](AsanStruct* inner_ptr) {
245             // This will crash and should be detected as a protected access.
246             inner_ptr->func();
247           },
248           base::Unretained(protected_ptr2)));
249 
250   // Both `inner_ptr` and `outer_ptr` are protected in these callbacks, since
251   // both are bound before `*ptr` is deleted. This test is making sure that
252   // `outer_ptr` is still treated as protected after `inner_callback` has run.
253   auto safe_nested_outer_callback = base::BindOnce(
254       [](AsanStruct* outer_ptr, base::OnceClosure inner_callback) {
255         std::move(inner_callback).Run();
256         // This will crash and should be detected as a protected access.
257         outer_ptr->func();
258       },
259       base::Unretained(protected_ptr),
260       base::BindOnce(
261           [](AsanStruct* inner_ptr) {
262             // Do nothing - we don't want to trip the protection inside the
263             // inner callback.
264           },
265           base::Unretained(protected_ptr2)));
266 
267   // Now delete `*protected_ptr` and check that the callbacks we created are
268   // handled correctly.
269   delete protected_ptr.get();
270   delete protected_ptr2.get();
271   protected_ptr = nullptr;
272   protected_ptr2 = nullptr;
273 
274   EXPECT_DEATH_IF_SUPPORTED(std::move(safe_callback).Run(),
275                             kAsanBrpProtected_Callback);
276   EXPECT_DEATH_IF_SUPPORTED(std::move(safe_nested_inner_callback).Run(),
277                             kAsanBrpProtected_Callback);
278   EXPECT_DEATH_IF_SUPPORTED(std::move(safe_nested_outer_callback).Run(),
279                             kAsanBrpProtected_Callback);
280 }
281 
TEST_F(AsanBackupRefPtrTest,BoundArgumentsNotProtected)282 TEST_F(AsanBackupRefPtrTest, BoundArgumentsNotProtected) {
283   raw_ptr<AsanStruct> protected_ptr = new AsanStruct;
284 
285   // First create our test callbacks while `*protected_ptr` is still valid, and
286   // we will then invoke them after deleting `*protected_ptr`.
287 
288   // `ptr` is not protected in this callback after `*ptr` has been deleted, as
289   // integer-type bind arguments do not use an internal `raw_ptr<T>`.
290   auto unsafe_callback = base::BindOnce(
291       [](uintptr_t address) {
292         AsanStruct* ptr = reinterpret_cast<AsanStruct*>(address);
293         // This will crash and should not be detected as a protected access.
294         ptr->func();
295       },
296       reinterpret_cast<uintptr_t>(protected_ptr.get()));
297 
298   // In this case, `outer_ptr` is protected in these callbacks, since it is
299   // bound before `*ptr` is deleted. We want to make sure that the access to
300   // `inner_ptr` is not automatically treated as protected (although it actually
301   // is) because we're trying to limit the protection scope to be very
302   // conservative here.
303   auto unsafe_nested_inner_callback = base::BindOnce(
304       [](AsanStruct* outer_ptr, base::OnceClosure inner_callback) {
305         std::move(inner_callback).Run();
306         // This will never be executed, as we will crash in inner_callback
307         NOTREACHED();
308       },
309       base::Unretained(protected_ptr),
310       base::BindOnce(
311           [](uintptr_t inner_address) {
312             AsanStruct* inner_ptr =
313                 reinterpret_cast<AsanStruct*>(inner_address);
314             // This will crash and should be detected as maybe protected, since
315             // it follows an extraction operation when the outer callback is
316             // invoked
317             inner_ptr->func();
318           },
319           reinterpret_cast<uintptr_t>(protected_ptr.get())));
320 
321   // In this case, `inner_ptr` is protected in these callbacks, since it is
322   // bound before `*ptr` is deleted. We want to make sure that the access to
323   // `outer_ptr` is not automatically treated as protected, since it isn't.
324   auto unsafe_nested_outer_callback = base::BindOnce(
325       [](uintptr_t outer_address, base::OnceClosure inner_callback) {
326         { std::move(inner_callback).Run(); }
327         AsanStruct* outer_ptr = reinterpret_cast<AsanStruct*>(outer_address);
328         // This will crash and should be detected as maybe protected, since it
329         // follows an extraction operation when the inner callback is invoked.
330         outer_ptr->func();
331       },
332       reinterpret_cast<uintptr_t>(protected_ptr.get()),
333       base::BindOnce(
334           [](AsanStruct* inner_ptr) {
335             // Do nothing - we don't want to trip the protection inside the
336             // inner callback.
337           },
338           base::Unretained(protected_ptr)));
339 
340   // Now delete `*protected_ptr` and check that the callbacks we created are
341   // handled correctly.
342   delete protected_ptr.get();
343   protected_ptr = nullptr;
344 
345   EXPECT_DEATH_IF_SUPPORTED(std::move(unsafe_callback).Run(),
346                             kAsanBrpNotProtected_NoRawPtrAccess);
347   EXPECT_DEATH_IF_SUPPORTED(std::move(unsafe_nested_inner_callback).Run(),
348                             kAsanBrpMaybeProtected_Extraction);
349   EXPECT_DEATH_IF_SUPPORTED(std::move(unsafe_nested_outer_callback).Run(),
350                             kAsanBrpMaybeProtected_Extraction);
351 }
352 
TEST_F(AsanBackupRefPtrTest,BoundArgumentsInstantiation)353 TEST_F(AsanBackupRefPtrTest, BoundArgumentsInstantiation) {
354   // This test is ensuring that instantiations of `raw_ptr` inside callbacks are
355   // handled correctly.
356 
357   raw_ptr<AsanStruct> protected_ptr = new AsanStruct;
358 
359   // First create our test callback while `*protected_ptr` is still valid.
360   auto callback = base::BindRepeating(
361       [](AsanStruct* ptr) {
362         // This will crash if `*protected_ptr` is not valid.
363         [[maybe_unused]] raw_ptr<AsanStruct> copy_ptr = ptr;
364       },
365       base::Unretained(protected_ptr));
366 
367   // It is allowed to create a new `raw_ptr<T>` inside a callback while
368   // `*protected_ptr` is still valid.
369   callback.Run();
370 
371   delete protected_ptr.get();
372   protected_ptr = nullptr;
373 
374   // It is not allowed to create a new `raw_ptr<T>` inside a callback once
375   // `*protected_ptr` is no longer valid.
376   EXPECT_DEATH_IF_SUPPORTED(std::move(callback).Run(), kAsanBrp_Instantiation);
377 }
378 
TEST_F(AsanBackupRefPtrTest,BoundReferences)379 TEST_F(AsanBackupRefPtrTest, BoundReferences) {
380   auto ptr = ::std::make_unique<AsanStruct>();
381 
382   // This test is ensuring that reference parameters inside callbacks are
383   // handled correctly.
384 
385   // We should not crash during unwrapping a reference parameter if the
386   // parameter is not accessed inside the callback.
387   auto no_crash_callback = base::BindOnce(
388       [](AsanStruct& ref) {
389         // There should be no crash here as we don't access ref.
390       },
391       std::reference_wrapper(*ptr));
392 
393   // `ref` is protected in this callback even after `*ptr` has been deleted,
394   // since the allocation will be kept alive by the internal `raw_ref<T>` inside
395   // base::UnretainedRefWrapper().
396   auto callback = base::BindOnce(
397       [](AsanStruct& ref) {
398         // This will crash and should be detected as protected
399         ref.func();
400       },
401       std::reference_wrapper(*ptr));
402 
403   ptr.reset();
404 
405   std::move(no_crash_callback).Run();
406 
407   EXPECT_DEATH_IF_SUPPORTED(std::move(callback).Run(),
408                             kAsanBrpProtected_Callback);
409 }
410 
TEST_F(AsanBackupRefPtrTest,FreeOnAnotherThread)411 TEST_F(AsanBackupRefPtrTest, FreeOnAnotherThread) {
412   auto ptr = ::std::make_unique<AsanStruct>();
413   raw_ptr<AsanStruct> protected_ptr = ptr.get();
414 
415   std::thread thread([&ptr] { ptr.reset(); });
416   thread.join();
417 
418   EXPECT_DEATH_IF_SUPPORTED(protected_ptr->func(), kAsanBrpMaybeProtected_Race);
419 }
420 
TEST_F(AsanBackupRefPtrTest,AccessOnThreadPoolThread)421 TEST_F(AsanBackupRefPtrTest, AccessOnThreadPoolThread) {
422   auto ptr = ::std::make_unique<AsanStruct>();
423   raw_ptr<AsanStruct> protected_ptr = ptr.get();
424 
425   test::TaskEnvironment env;
426   RunLoop run_loop;
427 
428   ThreadPool::PostTaskAndReply(
429       FROM_HERE, {}, base::BindLambdaForTesting([&ptr, &protected_ptr] {
430         ptr.reset();
431         EXPECT_DEATH_IF_SUPPORTED(protected_ptr->func(),
432                                   kAsanBrpMaybeProtected_ThreadPool);
433       }),
434       base::BindLambdaForTesting([&run_loop]() { run_loop.Quit(); }));
435   run_loop.Run();
436 }
437 
TEST_F(AsanBackupRefPtrTest,DanglingUnretained)438 TEST_F(AsanBackupRefPtrTest, DanglingUnretained) {
439   // The test should finish without crashing.
440 
441   raw_ptr<AsanStruct> protected_ptr = new AsanStruct;
442   delete protected_ptr.get();
443 
444   auto ptr_callback = base::BindOnce(
445       [](AsanStruct* ptr) {
446         // Do nothing - we only check the behavior of `BindOnce` in this test.
447       },
448       protected_ptr);
449 }
450 
451 }  // namespace base::internal
452 
453 #endif  // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
454