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