• 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 #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