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