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