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 
18 using namespace lldb;
19 using namespace lldb_private;
20 
21 const char *address_sanitizer_retrieve_report_data_prefix = R"(
22 extern "C"
23 {
24 int __asan_report_present();
25 void *__asan_get_report_pc();
26 void *__asan_get_report_bp();
27 void *__asan_get_report_sp();
28 void *__asan_get_report_address();
29 const char *__asan_get_report_description();
30 int __asan_get_report_access_type();
31 size_t __asan_get_report_access_size();
32 }
33 )";
34 
35 const char *address_sanitizer_retrieve_report_data_command = R"(
36 struct {
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 
47 t.present = __asan_report_present();
48 t.access_type = __asan_get_report_access_type();
49 t.pc = __asan_get_report_pc();
50 t.bp = __asan_get_report_bp();
51 t.sp = __asan_get_report_sp();
52 t.address = __asan_get_report_address();
53 t.access_size = __asan_get_report_access_size();
54 t.description = __asan_get_report_description();
55 t
56 )";
57 
58 StructuredData::ObjectSP
RetrieveReportData(const ProcessSP process_sp)59 ReportRetriever::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 
144 std::string
FormatDescription(StructuredData::ObjectSP report)145 ReportRetriever::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 
NotifyBreakpointHit(ProcessSP process_sp,StoppointCallbackContext * context,user_id_t break_id,user_id_t break_loc_id)191 bool 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 
SetupBreakpoint(ModuleSP module_sp,ProcessSP process_sp,ConstString symbol_name)222 Breakpoint *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