| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/debug/asan_service.h" |
| |
| #if defined(ADDRESS_SANITIZER) |
| |
| #include <map> |
| #include <memory> |
| #include <sstream> |
| |
| #include "base/debug/asan_invalid_access.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_piece.h" |
| #include "base/test/bind.h" |
| #include "base/test/task_environment.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // All of the tests require death tests, so there's nothing to build if we're |
| // building for a platform that doesn't support them. |
| #if defined(GTEST_HAS_DEATH_TEST) |
| |
| namespace base { |
| namespace debug { |
| |
| class AsanServiceTest : public ::testing::Test { |
| protected: |
| void SetUp() override { AsanService::GetInstance()->Initialize(); } |
| }; |
| |
| bool ExitedCleanly(int exit_status) { |
| return exit_status == 0; |
| } |
| |
| // TODO(crbug.com/1402267): ASAN death test is not picking up the failure |
| // in the emulator logs. Disabling to keep ASAN queue clear. |
| #if BUILDFLAG(IS_FUCHSIA) |
| #define MAYBE_ErrorCallback DISABLED_ErrorCallback |
| #define MAYBE_CrashInErrorCallback DISABLED_CrashInErrorCallback |
| #define MAYBE_ShouldExitCleanly DISABLED_ShouldExitCleanly |
| #define MAYBE_TaskTraceCallback DISABLED_TaskTraceCallback |
| #else |
| #define MAYBE_ErrorCallback ErrorCallback |
| #define MAYBE_CrashInErrorCallback CrashInErrorCallback |
| #define MAYBE_ShouldExitCleanly ShouldExitCleanly |
| #define MAYBE_TaskTraceCallback TaskTraceCallback |
| #endif |
| |
| TEST_F(AsanServiceTest, MAYBE_ErrorCallback) { |
| // Register an error callback, and check that the output is added. |
| AsanService::GetInstance()->AddErrorCallback([](const char*, bool*) { |
| AsanService::GetInstance()->Log("\nErrorCallback1"); |
| }); |
| EXPECT_DEATH(AsanHeapUseAfterFree(), "ErrorCallback1"); |
| |
| // Register a second error callback, and check that the output from both |
| // callbacks is added. |
| AsanService::GetInstance()->AddErrorCallback([](const char*, bool*) { |
| AsanService::GetInstance()->Log("\nErrorCallback2"); |
| }); |
| EXPECT_DEATH(AsanHeapUseAfterFree(), "ErrorCallback1"); |
| EXPECT_DEATH(AsanHeapUseAfterFree(), "ErrorCallback2"); |
| } |
| |
| TEST_F(AsanServiceTest, MAYBE_CrashInErrorCallback) { |
| // If a nested fault happens, we don't expect to get our custom log messages |
| // displayed, but we should still get some part of the ASan report. This |
| // matches current ASan recursive fault handling - make sure we don't end up |
| // deadlocking. |
| AsanService::GetInstance()->AddErrorCallback([](const char*, bool*) { |
| AsanService::GetInstance()->Log("\nErrorCallback1"); |
| AsanHeapUseAfterFree(); |
| }); |
| EXPECT_DEATH(AsanHeapUseAfterFree(), |
| "AddressSanitizer: nested bug in the same thread"); |
| } |
| |
| TEST_F(AsanServiceTest, MAYBE_ShouldExitCleanly) { |
| // Register an error callback, and check that the output is added. |
| AsanService::GetInstance()->AddErrorCallback([](const char*, bool*) { |
| AsanService::GetInstance()->Log("\nErrorCallback1"); |
| }); |
| EXPECT_DEATH(AsanHeapUseAfterFree(), "ErrorCallback1"); |
| EXPECT_DEATH(AsanHeapUseAfterFree(), "ABORTING"); |
| |
| // Register a second error callback which will set should_exit_cleanly. |
| AsanService::GetInstance()->AddErrorCallback( |
| [](const char* reason, bool* should_exit_cleanly) { |
| AsanService::GetInstance()->Log("\nShouldExitCleanly"); |
| *should_exit_cleanly = true; |
| }); |
| |
| // Check that we now exit instead of crashing. |
| EXPECT_EXIT(AsanHeapUseAfterFree(), ExitedCleanly, "ErrorCallback1"); |
| EXPECT_EXIT(AsanHeapUseAfterFree(), ExitedCleanly, "ShouldExitCleanly"); |
| EXPECT_EXIT(AsanHeapUseAfterFree(), ExitedCleanly, "EXITING"); |
| } |
| |
| class AsanTaskTraceTest { |
| public: |
| AsanTaskTraceTest() {} |
| |
| void Run() { |
| task_runner_.PostTask( |
| FROM_HERE, BindOnce(&AsanTaskTraceTest::PostingTask, Unretained(this))); |
| task_environment_.RunUntilIdle(); |
| } |
| |
| private: |
| void PostingTask() { |
| task_runner_.PostTask(FROM_HERE, BindOnce(&AsanHeapUseAfterFree)); |
| } |
| |
| test::TaskEnvironment task_environment_; |
| SingleThreadTaskRunner& task_runner_ = |
| *task_environment_.GetMainThreadTaskRunner(); |
| }; |
| |
| TEST_F(AsanServiceTest, MAYBE_TaskTraceCallback) { |
| AsanTaskTraceTest test; |
| // We can't check the symbolization of the task trace, as this will fail on |
| // build configurations that don't include symbols. We instead just check |
| // that the task trace has the correct number of entries. |
| EXPECT_DEATH(test.Run(), "#0 0x.* .*\\n\\s+#1 0x.*"); |
| } |
| |
| } // namespace debug |
| } // namespace base |
| |
| #endif // defined(GTEST_HAS_DEATH_TEST) |
| #endif // ADDRESS_SANITIZER |