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