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