#ifdef UNSAFE_BUFFERS_BUILD
#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
#if defined(COMPONENT_BUILD) && BUILDFLAG(IS_WIN)
#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
namespace base {
namespace debug {
namespace {
NO_SANITIZE("address")
void TaskTraceErrorCallback(const char* error, bool*) {
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("");
}
}
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()) {
HANDLE log_handle = logging::DuplicateLogFileHandle();
if (log_handle) {
__sanitizer_set_report_fd(reinterpret_cast<void*>(log_handle));
}
}
#endif
__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);
__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;
{
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);
}
}
NO_SANITIZE("address")
void AsanService::ErrorReportCallback(const char* reason) {
AsanService::GetInstance()->RunErrorCallbacks(reason);
}
AsanService::AsanService() {}
}
}
#endif