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