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