1 //===-- UBSanRuntime.cpp ----------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "UBSanRuntime.h"
11 
12 #include "Plugins/Process/Utility/HistoryThread.h"
13 #include "lldb/Breakpoint/StoppointCallbackContext.h"
14 #include "lldb/Core/Debugger.h"
15 #include "lldb/Core/Module.h"
16 #include "lldb/Core/PluginInterface.h"
17 #include "lldb/Core/PluginManager.h"
18 #include "lldb/Core/StreamFile.h"
19 #include "lldb/Core/ValueObject.h"
20 #include "lldb/Expression/UserExpression.h"
21 #include "lldb/Interpreter/CommandReturnObject.h"
22 #include "lldb/Symbol/Symbol.h"
23 #include "lldb/Symbol/SymbolContext.h"
24 #include "lldb/Symbol/Variable.h"
25 #include "lldb/Symbol/VariableList.h"
26 #include "lldb/Target/InstrumentationRuntimeStopInfo.h"
27 #include "lldb/Target/SectionLoadList.h"
28 #include "lldb/Target/StopInfo.h"
29 #include "lldb/Target/Target.h"
30 #include "lldb/Target/Thread.h"
31 #include "lldb/Utility/RegularExpression.h"
32 #include "lldb/Utility/Stream.h"
33 #include <ctype.h>
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 
~UndefinedBehaviorSanitizerRuntime()38 UndefinedBehaviorSanitizerRuntime::~UndefinedBehaviorSanitizerRuntime() {
39   Deactivate();
40 }
41 
42 lldb::InstrumentationRuntimeSP
CreateInstance(const lldb::ProcessSP & process_sp)43 UndefinedBehaviorSanitizerRuntime::CreateInstance(
44     const lldb::ProcessSP &process_sp) {
45   return InstrumentationRuntimeSP(
46       new UndefinedBehaviorSanitizerRuntime(process_sp));
47 }
48 
Initialize()49 void UndefinedBehaviorSanitizerRuntime::Initialize() {
50   PluginManager::RegisterPlugin(
51       GetPluginNameStatic(),
52       "UndefinedBehaviorSanitizer instrumentation runtime plugin.",
53       CreateInstance, GetTypeStatic);
54 }
55 
Terminate()56 void UndefinedBehaviorSanitizerRuntime::Terminate() {
57   PluginManager::UnregisterPlugin(CreateInstance);
58 }
59 
60 lldb_private::ConstString
GetPluginNameStatic()61 UndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() {
62   return ConstString("UndefinedBehaviorSanitizer");
63 }
64 
65 lldb::InstrumentationRuntimeType
GetTypeStatic()66 UndefinedBehaviorSanitizerRuntime::GetTypeStatic() {
67   return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer;
68 }
69 
70 static const char *ub_sanitizer_retrieve_report_data_prefix = R"(
71 extern "C" {
72 void
73 __ubsan_get_current_report_data(const char **OutIssueKind,
74     const char **OutMessage, const char **OutFilename, unsigned *OutLine,
75     unsigned *OutCol, char **OutMemoryAddr);
76 }
77 
78 struct data {
79   const char *issue_kind;
80   const char *message;
81   const char *filename;
82   unsigned line;
83   unsigned col;
84   char *memory_addr;
85 };
86 )";
87 
88 static const char *ub_sanitizer_retrieve_report_data_command = R"(
89 data t;
90 __ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line,
91                                 &t.col, &t.memory_addr);
92 t;
93 )";
94 
RetrieveUnsigned(ValueObjectSP return_value_sp,ProcessSP process_sp,const std::string & expression_path)95 static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp,
96                                ProcessSP process_sp,
97                                const std::string &expression_path) {
98   return return_value_sp->GetValueForExpressionPath(expression_path.c_str())
99       ->GetValueAsUnsigned(0);
100 }
101 
RetrieveString(ValueObjectSP return_value_sp,ProcessSP process_sp,const std::string & expression_path)102 static std::string RetrieveString(ValueObjectSP return_value_sp,
103                                   ProcessSP process_sp,
104                                   const std::string &expression_path) {
105   addr_t ptr = RetrieveUnsigned(return_value_sp, process_sp, expression_path);
106   std::string str;
107   Status error;
108   process_sp->ReadCStringFromMemory(ptr, str, error);
109   return str;
110 }
111 
RetrieveReportData(ExecutionContextRef exe_ctx_ref)112 StructuredData::ObjectSP UndefinedBehaviorSanitizerRuntime::RetrieveReportData(
113     ExecutionContextRef exe_ctx_ref) {
114   ProcessSP process_sp = GetProcessSP();
115   if (!process_sp)
116     return StructuredData::ObjectSP();
117 
118   ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
119   StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
120   ModuleSP runtime_module_sp = GetRuntimeModuleSP();
121   Target &target = process_sp->GetTarget();
122 
123   if (!frame_sp)
124     return StructuredData::ObjectSP();
125 
126   StreamFileSP Stream(target.GetDebugger().GetOutputFile());
127 
128   EvaluateExpressionOptions options;
129   options.SetUnwindOnError(true);
130   options.SetTryAllThreads(true);
131   options.SetStopOthers(true);
132   options.SetIgnoreBreakpoints(true);
133   options.SetTimeout(std::chrono::seconds(2));
134   options.SetPrefix(ub_sanitizer_retrieve_report_data_prefix);
135   options.SetAutoApplyFixIts(false);
136   options.SetLanguage(eLanguageTypeObjC_plus_plus);
137 
138   ValueObjectSP main_value;
139   ExecutionContext exe_ctx;
140   Status eval_error;
141   frame_sp->CalculateExecutionContext(exe_ctx);
142   ExpressionResults result = UserExpression::Evaluate(
143       exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "",
144       main_value, eval_error);
145   if (result != eExpressionCompleted) {
146     target.GetDebugger().GetAsyncOutputStream()->Printf(
147         "Warning: Cannot evaluate UndefinedBehaviorSanitizer expression:\n%s\n",
148         eval_error.AsCString());
149     return StructuredData::ObjectSP();
150   }
151 
152   // Gather the PCs of the user frames in the backtrace.
153   StructuredData::Array *trace = new StructuredData::Array();
154   auto trace_sp = StructuredData::ObjectSP(trace);
155   for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
156     const Address FCA =
157         thread_sp->GetStackFrameAtIndex(I)->GetFrameCodeAddress();
158     if (FCA.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
159       continue;
160 
161     lldb::addr_t PC = FCA.GetLoadAddress(&target);
162     trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
163   }
164 
165   std::string IssueKind = RetrieveString(main_value, process_sp, ".issue_kind");
166   std::string ErrMessage = RetrieveString(main_value, process_sp, ".message");
167   std::string Filename = RetrieveString(main_value, process_sp, ".filename");
168   unsigned Line = RetrieveUnsigned(main_value, process_sp, ".line");
169   unsigned Col = RetrieveUnsigned(main_value, process_sp, ".col");
170   uintptr_t MemoryAddr =
171       RetrieveUnsigned(main_value, process_sp, ".memory_addr");
172 
173   auto *d = new StructuredData::Dictionary();
174   auto dict_sp = StructuredData::ObjectSP(d);
175   d->AddStringItem("instrumentation_class", "UndefinedBehaviorSanitizer");
176   d->AddStringItem("description", IssueKind);
177   d->AddStringItem("summary", ErrMessage);
178   d->AddStringItem("filename", Filename);
179   d->AddIntegerItem("line", Line);
180   d->AddIntegerItem("col", Col);
181   d->AddIntegerItem("memory_address", MemoryAddr);
182   d->AddIntegerItem("tid", thread_sp->GetID());
183   d->AddItem("trace", trace_sp);
184   return dict_sp;
185 }
186 
GetStopReasonDescription(StructuredData::ObjectSP report)187 static std::string GetStopReasonDescription(StructuredData::ObjectSP report) {
188   llvm::StringRef stop_reason_description_ref;
189   report->GetAsDictionary()->GetValueForKeyAsString("description",
190                                                     stop_reason_description_ref);
191   std::string stop_reason_description = stop_reason_description_ref;
192 
193   if (!stop_reason_description.size()) {
194     stop_reason_description = "Undefined behavior detected";
195   } else {
196     stop_reason_description[0] = toupper(stop_reason_description[0]);
197     for (unsigned I = 1; I < stop_reason_description.size(); ++I)
198       if (stop_reason_description[I] == '-')
199         stop_reason_description[I] = ' ';
200   }
201   return stop_reason_description;
202 }
203 
NotifyBreakpointHit(void * baton,StoppointCallbackContext * context,user_id_t break_id,user_id_t break_loc_id)204 bool UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit(
205     void *baton, StoppointCallbackContext *context, user_id_t break_id,
206     user_id_t break_loc_id) {
207   assert(baton && "null baton");
208   if (!baton)
209     return false; //< false => resume execution.
210 
211   UndefinedBehaviorSanitizerRuntime *const instance =
212       static_cast<UndefinedBehaviorSanitizerRuntime *>(baton);
213 
214   ProcessSP process_sp = instance->GetProcessSP();
215   ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
216   if (!process_sp || !thread_sp ||
217       process_sp != context->exe_ctx_ref.GetProcessSP())
218     return false;
219 
220   if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
221     return false;
222 
223   StructuredData::ObjectSP report =
224       instance->RetrieveReportData(context->exe_ctx_ref);
225 
226   if (report) {
227     thread_sp->SetStopInfo(
228         InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
229             *thread_sp, GetStopReasonDescription(report), report));
230     return true;
231   }
232 
233   return false;
234 }
235 
236 const RegularExpression &
GetPatternForRuntimeLibrary()237 UndefinedBehaviorSanitizerRuntime::GetPatternForRuntimeLibrary() {
238   static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_"));
239   return regex;
240 }
241 
CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp)242 bool UndefinedBehaviorSanitizerRuntime::CheckIfRuntimeIsValid(
243     const lldb::ModuleSP module_sp) {
244   static ConstString ubsan_test_sym("__ubsan_on_report");
245   const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
246       ubsan_test_sym, lldb::eSymbolTypeAny);
247   return symbol != nullptr;
248 }
249 
250 // FIXME: Factor out all the logic we have in common with the {a,t}san plugins.
Activate()251 void UndefinedBehaviorSanitizerRuntime::Activate() {
252   if (IsActive())
253     return;
254 
255   ProcessSP process_sp = GetProcessSP();
256   if (!process_sp)
257     return;
258 
259   ModuleSP runtime_module_sp = GetRuntimeModuleSP();
260 
261   ConstString symbol_name("__ubsan_on_report");
262   const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
263       symbol_name, eSymbolTypeCode);
264 
265   if (symbol == nullptr)
266     return;
267 
268   if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
269     return;
270 
271   Target &target = process_sp->GetTarget();
272   addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
273 
274   if (symbol_address == LLDB_INVALID_ADDRESS)
275     return;
276 
277   Breakpoint *breakpoint =
278       process_sp->GetTarget()
279           .CreateBreakpoint(symbol_address, /*internal=*/true,
280                             /*hardware=*/false)
281           .get();
282   breakpoint->SetCallback(
283       UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit, this, true);
284   breakpoint->SetBreakpointKind("undefined-behavior-sanitizer-report");
285   SetBreakpointID(breakpoint->GetID());
286 
287   SetActive(true);
288 }
289 
Deactivate()290 void UndefinedBehaviorSanitizerRuntime::Deactivate() {
291   SetActive(false);
292 
293   auto BID = GetBreakpointID();
294   if (BID == LLDB_INVALID_BREAK_ID)
295     return;
296 
297   if (ProcessSP process_sp = GetProcessSP()) {
298     process_sp->GetTarget().RemoveBreakpointByID(BID);
299     SetBreakpointID(LLDB_INVALID_BREAK_ID);
300   }
301 }
302 
303 lldb::ThreadCollectionSP
GetBacktracesFromExtendedStopInfo(StructuredData::ObjectSP info)304 UndefinedBehaviorSanitizerRuntime::GetBacktracesFromExtendedStopInfo(
305     StructuredData::ObjectSP info) {
306   ThreadCollectionSP threads;
307   threads.reset(new ThreadCollection());
308 
309   ProcessSP process_sp = GetProcessSP();
310 
311   if (info->GetObjectForDotSeparatedPath("instrumentation_class")
312           ->GetStringValue() != "UndefinedBehaviorSanitizer")
313     return threads;
314 
315   std::vector<lldb::addr_t> PCs;
316   auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
317   trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
318     PCs.push_back(PC->GetAsInteger()->GetValue());
319     return true;
320   });
321 
322   if (PCs.empty())
323     return threads;
324 
325   StructuredData::ObjectSP thread_id_obj =
326       info->GetObjectForDotSeparatedPath("tid");
327   tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
328 
329   uint32_t stop_id = 0;
330   bool stop_id_is_valid = false;
331   HistoryThread *history_thread =
332       new HistoryThread(*process_sp, tid, PCs, stop_id, stop_id_is_valid);
333   ThreadSP new_thread_sp(history_thread);
334   std::string stop_reason_description = GetStopReasonDescription(info);
335   new_thread_sp->SetName(stop_reason_description.c_str());
336 
337   // Save this in the Process' ExtendedThreadList so a strong pointer retains
338   // the object
339   process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
340   threads->AddThread(new_thread_sp);
341 
342   return threads;
343 }
344