1//===-- ReportRetriever.cpp -----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ReportRetriever.h"
10
11#include "lldb/Breakpoint/StoppointCallbackContext.h"
12#include "lldb/Core/Debugger.h"
13#include "lldb/Core/Module.h"
14#include "lldb/Core/ValueObject.h"
15#include "lldb/Expression/UserExpression.h"
16#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
17
18using namespace lldb;
19using namespace lldb_private;
20
21const char *address_sanitizer_retrieve_report_data_prefix = R"(
22extern "C"
23{
24int __asan_report_present();
25void *__asan_get_report_pc();
26void *__asan_get_report_bp();
27void *__asan_get_report_sp();
28void *__asan_get_report_address();
29const char *__asan_get_report_description();
30int __asan_get_report_access_type();
31size_t __asan_get_report_access_size();
32}
33)";
34
35const char *address_sanitizer_retrieve_report_data_command = R"(
36struct {
37    int present;
38    int access_type;
39    void *pc;
40    void *bp;
41    void *sp;
42    void *address;
43    size_t access_size;
44    const char *description;
45} t;
46
47t.present = __asan_report_present();
48t.access_type = __asan_get_report_access_type();
49t.pc = __asan_get_report_pc();
50t.bp = __asan_get_report_bp();
51t.sp = __asan_get_report_sp();
52t.address = __asan_get_report_address();
53t.access_size = __asan_get_report_access_size();
54t.description = __asan_get_report_description();
55t
56)";
57
58StructuredData::ObjectSP
59ReportRetriever::RetrieveReportData(const ProcessSP process_sp) {
60  if (!process_sp)
61    return StructuredData::ObjectSP();
62
63  ThreadSP thread_sp =
64      process_sp->GetThreadList().GetExpressionExecutionThread();
65
66  if (!thread_sp)
67    return StructuredData::ObjectSP();
68
69  StackFrameSP frame_sp =
70      thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
71
72  if (!frame_sp)
73    return StructuredData::ObjectSP();
74
75  EvaluateExpressionOptions options;
76  options.SetUnwindOnError(true);
77  options.SetTryAllThreads(true);
78  options.SetStopOthers(true);
79  options.SetIgnoreBreakpoints(true);
80  options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
81  options.SetPrefix(address_sanitizer_retrieve_report_data_prefix);
82  options.SetAutoApplyFixIts(false);
83  options.SetLanguage(eLanguageTypeObjC_plus_plus);
84
85  ValueObjectSP return_value_sp;
86  ExecutionContext exe_ctx;
87  Status eval_error;
88  frame_sp->CalculateExecutionContext(exe_ctx);
89  ExpressionResults result = UserExpression::Evaluate(
90      exe_ctx, options, address_sanitizer_retrieve_report_data_command, "",
91      return_value_sp, eval_error);
92  if (result != eExpressionCompleted) {
93    StreamString ss;
94    ss << "cannot evaluate AddressSanitizer expression:\n";
95    ss << eval_error.AsCString();
96    Debugger::ReportWarning(ss.GetString().str(),
97                            process_sp->GetTarget().GetDebugger().GetID());
98    return StructuredData::ObjectSP();
99  }
100
101  int present = return_value_sp->GetValueForExpressionPath(".present")
102                    ->GetValueAsUnsigned(0);
103  if (present != 1)
104    return StructuredData::ObjectSP();
105
106  addr_t pc =
107      return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0);
108  addr_t bp =
109      return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0);
110  addr_t sp =
111      return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0);
112  addr_t address = return_value_sp->GetValueForExpressionPath(".address")
113                       ->GetValueAsUnsigned(0);
114  addr_t access_type =
115      return_value_sp->GetValueForExpressionPath(".access_type")
116          ->GetValueAsUnsigned(0);
117  addr_t access_size =
118      return_value_sp->GetValueForExpressionPath(".access_size")
119          ->GetValueAsUnsigned(0);
120  addr_t description_ptr =
121      return_value_sp->GetValueForExpressionPath(".description")
122          ->GetValueAsUnsigned(0);
123  std::string description;
124  Status error;
125  process_sp->ReadCStringFromMemory(description_ptr, description, error);
126
127  auto dict = std::make_shared<StructuredData::Dictionary>();
128  if (!dict)
129    return StructuredData::ObjectSP();
130
131  dict->AddStringItem("instrumentation_class", "AddressSanitizer");
132  dict->AddStringItem("stop_type", "fatal_error");
133  dict->AddIntegerItem("pc", pc);
134  dict->AddIntegerItem("bp", bp);
135  dict->AddIntegerItem("sp", sp);
136  dict->AddIntegerItem("address", address);
137  dict->AddIntegerItem("access_type", access_type);
138  dict->AddIntegerItem("access_size", access_size);
139  dict->AddStringItem("description", description);
140
141  return StructuredData::ObjectSP(dict);
142}
143
144std::string
145ReportRetriever::FormatDescription(StructuredData::ObjectSP report) {
146  std::string description = std::string(report->GetAsDictionary()
147                                            ->GetValueForKey("description")
148                                            ->GetAsString()
149                                            ->GetValue());
150  return llvm::StringSwitch<std::string>(description)
151      .Case("heap-use-after-free", "Use of deallocated memory")
152      .Case("heap-buffer-overflow", "Heap buffer overflow")
153      .Case("stack-buffer-underflow", "Stack buffer underflow")
154      .Case("initialization-order-fiasco", "Initialization order problem")
155      .Case("stack-buffer-overflow", "Stack buffer overflow")
156      .Case("stack-use-after-return", "Use of stack memory after return")
157      .Case("use-after-poison", "Use of poisoned memory")
158      .Case("container-overflow", "Container overflow")
159      .Case("stack-use-after-scope", "Use of out-of-scope stack memory")
160      .Case("global-buffer-overflow", "Global buffer overflow")
161      .Case("unknown-crash", "Invalid memory access")
162      .Case("stack-overflow", "Stack space exhausted")
163      .Case("null-deref", "Dereference of null pointer")
164      .Case("wild-jump", "Jump to non-executable address")
165      .Case("wild-addr-write", "Write through wild pointer")
166      .Case("wild-addr-read", "Read from wild pointer")
167      .Case("wild-addr", "Access through wild pointer")
168      .Case("signal", "Deadly signal")
169      .Case("double-free", "Deallocation of freed memory")
170      .Case("new-delete-type-mismatch",
171            "Deallocation size different from allocation size")
172      .Case("bad-free", "Deallocation of non-allocated memory")
173      .Case("alloc-dealloc-mismatch",
174            "Mismatch between allocation and deallocation APIs")
175      .Case("bad-malloc_usable_size", "Invalid argument to malloc_usable_size")
176      .Case("bad-__sanitizer_get_allocated_size",
177            "Invalid argument to __sanitizer_get_allocated_size")
178      .Case("param-overlap",
179            "Call to function disallowing overlapping memory ranges")
180      .Case("negative-size-param", "Negative size used when accessing memory")
181      .Case("bad-__sanitizer_annotate_contiguous_container",
182            "Invalid argument to __sanitizer_annotate_contiguous_container")
183      .Case("odr-violation", "Symbol defined in multiple translation units")
184      .Case(
185          "invalid-pointer-pair",
186          "Comparison or arithmetic on pointers from different memory regions")
187      // for unknown report codes just show the code
188      .Default("AddressSanitizer detected: " + description);
189}
190
191bool ReportRetriever::NotifyBreakpointHit(ProcessSP process_sp,
192                                          StoppointCallbackContext *context,
193                                          user_id_t break_id,
194                                          user_id_t break_loc_id) {
195  // Make sure this is the right process
196  if (!process_sp || process_sp != context->exe_ctx_ref.GetProcessSP())
197    return false;
198
199  if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
200    return false;
201
202  StructuredData::ObjectSP report = RetrieveReportData(process_sp);
203  if (!report || report->GetType() != lldb::eStructuredDataTypeDictionary)
204    return false;
205
206  std::string description = FormatDescription(report);
207
208  if (ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP())
209    thread_sp->SetStopInfo(
210        InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
211            *thread_sp, description, report));
212
213  if (StreamFileSP stream_sp = StreamFileSP(
214          process_sp->GetTarget().GetDebugger().GetOutputStreamSP()))
215    stream_sp->Printf("AddressSanitizer report breakpoint hit. Use 'thread "
216                      "info -s' to get extended information about the "
217                      "report.\n");
218
219  return true; // Return true to stop the target
220}
221
222Breakpoint *ReportRetriever::SetupBreakpoint(ModuleSP module_sp,
223                                             ProcessSP process_sp,
224                                             ConstString symbol_name) {
225  if (!module_sp || !process_sp)
226    return nullptr;
227
228  const Symbol *symbol =
229      module_sp->FindFirstSymbolWithNameAndType(symbol_name, eSymbolTypeCode);
230
231  if (symbol == nullptr)
232    return nullptr;
233
234  if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
235    return nullptr;
236
237  Target &target = process_sp->GetTarget();
238  addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
239
240  if (symbol_address == LLDB_INVALID_ADDRESS)
241    return nullptr;
242
243  const bool internal = true;
244  const bool hardware = false;
245
246  Breakpoint *breakpoint =
247      process_sp->GetTarget()
248          .CreateBreakpoint(symbol_address, internal, hardware)
249          .get();
250
251  return breakpoint;
252}
253