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