1 // Copyright 2022 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/debug/asan_service.h"
6
7 #if defined(ADDRESS_SANITIZER)
8 #include <sanitizer/asan_interface.h>
9
10 #include "base/debug/task_trace.h"
11 #include "base/no_destructor.h"
12 #include "base/process/process.h"
13 #include "base/process/process_handle.h"
14 #include "base/strings/stringprintf.h"
15
16 #if defined(COMPONENT_BUILD) && defined(_WIN32)
17 // In component builds on Windows, weak function exported by ASan have the
18 // `__dll` suffix. ASan itself uses the `alternatename` directive to account for
19 // that.
20 #pragma comment(linker, \
21 "/alternatename:__sanitizer_report_error_summary=" \
22 "__sanitizer_report_error_summary__dll")
23 #endif // defined(COMPONENT_BUILD) && defined(_WIN32)
24
25 namespace base {
26 namespace debug {
27
28 namespace {
29 NO_SANITIZE("address")
TaskTraceErrorCallback(const char * error,bool *)30 void TaskTraceErrorCallback(const char* error, bool*) {
31 // Use the sanitizer api to symbolize the task trace, which otherwise might
32 // not symbolize properly. This also lets us format the task trace in the
33 // same way as the address sanitizer backtraces, which also means that we can
34 // get the stack trace symbolized with asan_symbolize.py in the cases where
35 // symbolization at runtime fails.
36 std::array<const void*, 4> addresses;
37 size_t address_count = TaskTrace().GetAddresses(addresses);
38
39 AsanService::GetInstance()->Log("Task trace:");
40 size_t frame_index = 0;
41 for (size_t i = 0; i < std::min(address_count, addresses.size()); ++i) {
42 char buffer[4096] = {};
43 void* address = const_cast<void*>(addresses[i]);
44 __sanitizer_symbolize_pc(address, "%p %F %L", buffer, sizeof(buffer));
45 for (char* ptr = buffer; *ptr != 0; ptr += strlen(ptr)) {
46 AsanService::GetInstance()->Log(" #%i %s", frame_index++, ptr);
47 }
48 }
49 AsanService::GetInstance()->Log("");
50 }
51 } // namespace
52
53 // static
54 NO_SANITIZE("address")
GetInstance()55 AsanService* AsanService::GetInstance() {
56 static NoDestructor<AsanService> instance;
57 return instance.get();
58 }
59
Initialize()60 void AsanService::Initialize() {
61 AutoLock lock(lock_);
62 if (!is_initialized_) {
63 __asan_set_error_report_callback(ErrorReportCallback);
64 error_callbacks_.push_back(TaskTraceErrorCallback);
65 is_initialized_ = true;
66 }
67 }
68
69 NO_SANITIZE("address")
Log(const char * format,...)70 void AsanService::Log(const char* format, ...) {
71 va_list ap;
72 va_start(ap, format);
73 auto formatted_message = StringPrintV(format, ap);
74 va_end(ap);
75
76 // Despite its name, the function just prints the input to the destination
77 // configured by ASan.
78 __sanitizer_report_error_summary(formatted_message.c_str());
79 }
80
AddErrorCallback(ErrorCallback error_callback)81 void AsanService::AddErrorCallback(ErrorCallback error_callback) {
82 AutoLock lock(lock_);
83 CHECK(is_initialized_);
84 error_callbacks_.push_back(error_callback);
85 }
86
87 NO_SANITIZE("address")
RunErrorCallbacks(const char * reason)88 void AsanService::RunErrorCallbacks(const char* reason) {
89 ProcessId process_id = GetCurrentProcId();
90 bool should_exit_cleanly = false;
91
92 {
93 // We can hold `lock_` throughout the error callbacks, since ASan doesn't
94 // re-enter when handling nested errors on the same thread.
95 AutoLock lock(lock_);
96
97 Log("\n==%i==ADDITIONAL INFO", (int)process_id);
98 Log("\n==%i==Note: Please include this section with the ASan report.",
99 (int)process_id);
100 for (const auto& error_callback : error_callbacks_) {
101 error_callback(reason, &should_exit_cleanly);
102 }
103 Log("\n==%i==END OF ADDITIONAL INFO", (int)process_id);
104 }
105
106 if (should_exit_cleanly) {
107 Log("\n==%i==EXITING", (int)process_id);
108 Process::TerminateCurrentProcessImmediately(0);
109 }
110 }
111
112 // static
113 NO_SANITIZE("address")
ErrorReportCallback(const char * reason)114 void AsanService::ErrorReportCallback(const char* reason) {
115 AsanService::GetInstance()->RunErrorCallbacks(reason);
116 }
117
AsanService()118 AsanService::AsanService() {}
119
120 } // namespace debug
121 } // namespace base
122
123 #endif // defined(ADDRESS_SANITIZER)
124