• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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