/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/InstrumentationRuntime/ASan/InstrumentationRuntimeASan.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- InstrumentationRuntimeASan.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 "InstrumentationRuntimeASan.h" |
10 | | |
11 | | #include "lldb/Breakpoint/StoppointCallbackContext.h" |
12 | | #include "lldb/Core/Debugger.h" |
13 | | #include "lldb/Core/Module.h" |
14 | | #include "lldb/Core/PluginInterface.h" |
15 | | #include "lldb/Core/PluginManager.h" |
16 | | #include "lldb/Core/ValueObject.h" |
17 | | #include "lldb/Expression/UserExpression.h" |
18 | | #include "lldb/Host/StreamFile.h" |
19 | | #include "lldb/Interpreter/CommandReturnObject.h" |
20 | | #include "lldb/Symbol/Symbol.h" |
21 | | #include "lldb/Target/InstrumentationRuntimeStopInfo.h" |
22 | | #include "lldb/Target/StopInfo.h" |
23 | | #include "lldb/Target/Target.h" |
24 | | #include "lldb/Target/Thread.h" |
25 | | #include "lldb/Utility/RegularExpression.h" |
26 | | #include "lldb/Utility/Stream.h" |
27 | | |
28 | | #include "llvm/ADT/StringSwitch.h" |
29 | | |
30 | | using namespace lldb; |
31 | | using namespace lldb_private; |
32 | | |
33 | | LLDB_PLUGIN_DEFINE(InstrumentationRuntimeASan) |
34 | | |
35 | | lldb::InstrumentationRuntimeSP |
36 | 2.05k | InstrumentationRuntimeASan::CreateInstance(const lldb::ProcessSP &process_sp) { |
37 | 2.05k | return InstrumentationRuntimeSP(new InstrumentationRuntimeASan(process_sp)); |
38 | 2.05k | } |
39 | | |
40 | 3.91k | void InstrumentationRuntimeASan::Initialize() { |
41 | 3.91k | PluginManager::RegisterPlugin( |
42 | 3.91k | GetPluginNameStatic(), "AddressSanitizer instrumentation runtime plugin.", |
43 | 3.91k | CreateInstance, GetTypeStatic); |
44 | 3.91k | } |
45 | | |
46 | 3.90k | void InstrumentationRuntimeASan::Terminate() { |
47 | 3.90k | PluginManager::UnregisterPlugin(CreateInstance); |
48 | 3.90k | } |
49 | | |
50 | 8.33k | lldb::InstrumentationRuntimeType InstrumentationRuntimeASan::GetTypeStatic() { |
51 | 8.33k | return eInstrumentationRuntimeTypeAddressSanitizer; |
52 | 8.33k | } |
53 | | |
54 | 2.05k | InstrumentationRuntimeASan::~InstrumentationRuntimeASan() { Deactivate(); } |
55 | | |
56 | | const RegularExpression & |
57 | 104k | InstrumentationRuntimeASan::GetPatternForRuntimeLibrary() { |
58 | | // FIXME: This shouldn't include the "dylib" suffix. |
59 | 104k | static RegularExpression regex( |
60 | 104k | llvm::StringRef("libclang_rt.asan_(.*)_dynamic\\.dylib")); |
61 | 104k | return regex; |
62 | 104k | } |
63 | | |
64 | | bool InstrumentationRuntimeASan::CheckIfRuntimeIsValid( |
65 | 2.04k | const lldb::ModuleSP module_sp) { |
66 | 2.04k | const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( |
67 | 2.04k | ConstString("__asan_get_alloc_stack"), lldb::eSymbolTypeAny); |
68 | | |
69 | 2.04k | return symbol != nullptr; |
70 | 2.04k | } |
71 | | |
72 | | const char *address_sanitizer_retrieve_report_data_prefix = R"( |
73 | | extern "C" |
74 | | { |
75 | | int __asan_report_present(); |
76 | | void *__asan_get_report_pc(); |
77 | | void *__asan_get_report_bp(); |
78 | | void *__asan_get_report_sp(); |
79 | | void *__asan_get_report_address(); |
80 | | const char *__asan_get_report_description(); |
81 | | int __asan_get_report_access_type(); |
82 | | size_t __asan_get_report_access_size(); |
83 | | } |
84 | | )"; |
85 | | |
86 | | const char *address_sanitizer_retrieve_report_data_command = R"( |
87 | | struct { |
88 | | int present; |
89 | | int access_type; |
90 | | void *pc; |
91 | | void *bp; |
92 | | void *sp; |
93 | | void *address; |
94 | | size_t access_size; |
95 | | const char *description; |
96 | | } t; |
97 | | |
98 | | t.present = __asan_report_present(); |
99 | | t.access_type = __asan_get_report_access_type(); |
100 | | t.pc = __asan_get_report_pc(); |
101 | | t.bp = __asan_get_report_bp(); |
102 | | t.sp = __asan_get_report_sp(); |
103 | | t.address = __asan_get_report_address(); |
104 | | t.access_size = __asan_get_report_access_size(); |
105 | | t.description = __asan_get_report_description(); |
106 | | t |
107 | | )"; |
108 | | |
109 | 0 | StructuredData::ObjectSP InstrumentationRuntimeASan::RetrieveReportData() { |
110 | 0 | ProcessSP process_sp = GetProcessSP(); |
111 | 0 | if (!process_sp) |
112 | 0 | return StructuredData::ObjectSP(); |
113 | | |
114 | 0 | ThreadSP thread_sp = |
115 | 0 | process_sp->GetThreadList().GetExpressionExecutionThread(); |
116 | 0 | StackFrameSP frame_sp = |
117 | 0 | thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame); |
118 | |
|
119 | 0 | if (!frame_sp) |
120 | 0 | return StructuredData::ObjectSP(); |
121 | | |
122 | 0 | EvaluateExpressionOptions options; |
123 | 0 | options.SetUnwindOnError(true); |
124 | 0 | options.SetTryAllThreads(true); |
125 | 0 | options.SetStopOthers(true); |
126 | 0 | options.SetIgnoreBreakpoints(true); |
127 | 0 | options.SetTimeout(process_sp->GetUtilityExpressionTimeout()); |
128 | 0 | options.SetPrefix(address_sanitizer_retrieve_report_data_prefix); |
129 | 0 | options.SetAutoApplyFixIts(false); |
130 | 0 | options.SetLanguage(eLanguageTypeObjC_plus_plus); |
131 | |
|
132 | 0 | ValueObjectSP return_value_sp; |
133 | 0 | ExecutionContext exe_ctx; |
134 | 0 | Status eval_error; |
135 | 0 | frame_sp->CalculateExecutionContext(exe_ctx); |
136 | 0 | ExpressionResults result = UserExpression::Evaluate( |
137 | 0 | exe_ctx, options, address_sanitizer_retrieve_report_data_command, "", |
138 | 0 | return_value_sp, eval_error); |
139 | 0 | if (result != eExpressionCompleted) { |
140 | 0 | StreamString ss; |
141 | 0 | ss << "cannot evaluate AddressSanitizer expression:\n"; |
142 | 0 | ss << eval_error.AsCString(); |
143 | 0 | Debugger::ReportWarning(ss.GetString().str(), |
144 | 0 | process_sp->GetTarget().GetDebugger().GetID()); |
145 | 0 | return StructuredData::ObjectSP(); |
146 | 0 | } |
147 | | |
148 | 0 | int present = return_value_sp->GetValueForExpressionPath(".present") |
149 | 0 | ->GetValueAsUnsigned(0); |
150 | 0 | if (present != 1) |
151 | 0 | return StructuredData::ObjectSP(); |
152 | | |
153 | 0 | addr_t pc = |
154 | 0 | return_value_sp->GetValueForExpressionPath(".pc")->GetValueAsUnsigned(0); |
155 | 0 | addr_t bp = |
156 | 0 | return_value_sp->GetValueForExpressionPath(".bp")->GetValueAsUnsigned(0); |
157 | 0 | addr_t sp = |
158 | 0 | return_value_sp->GetValueForExpressionPath(".sp")->GetValueAsUnsigned(0); |
159 | 0 | addr_t address = return_value_sp->GetValueForExpressionPath(".address") |
160 | 0 | ->GetValueAsUnsigned(0); |
161 | 0 | addr_t access_type = |
162 | 0 | return_value_sp->GetValueForExpressionPath(".access_type") |
163 | 0 | ->GetValueAsUnsigned(0); |
164 | 0 | addr_t access_size = |
165 | 0 | return_value_sp->GetValueForExpressionPath(".access_size") |
166 | 0 | ->GetValueAsUnsigned(0); |
167 | 0 | addr_t description_ptr = |
168 | 0 | return_value_sp->GetValueForExpressionPath(".description") |
169 | 0 | ->GetValueAsUnsigned(0); |
170 | 0 | std::string description; |
171 | 0 | Status error; |
172 | 0 | process_sp->ReadCStringFromMemory(description_ptr, description, error); |
173 | |
|
174 | 0 | StructuredData::Dictionary *dict = new StructuredData::Dictionary(); |
175 | 0 | dict->AddStringItem("instrumentation_class", "AddressSanitizer"); |
176 | 0 | dict->AddStringItem("stop_type", "fatal_error"); |
177 | 0 | dict->AddIntegerItem("pc", pc); |
178 | 0 | dict->AddIntegerItem("bp", bp); |
179 | 0 | dict->AddIntegerItem("sp", sp); |
180 | 0 | dict->AddIntegerItem("address", address); |
181 | 0 | dict->AddIntegerItem("access_type", access_type); |
182 | 0 | dict->AddIntegerItem("access_size", access_size); |
183 | 0 | dict->AddStringItem("description", description); |
184 | |
|
185 | 0 | return StructuredData::ObjectSP(dict); |
186 | 0 | } |
187 | | |
188 | | std::string |
189 | 0 | InstrumentationRuntimeASan::FormatDescription(StructuredData::ObjectSP report) { |
190 | 0 | std::string description = std::string(report->GetAsDictionary() |
191 | 0 | ->GetValueForKey("description") |
192 | 0 | ->GetAsString() |
193 | 0 | ->GetValue()); |
194 | 0 | return llvm::StringSwitch<std::string>(description) |
195 | 0 | .Case("heap-use-after-free", "Use of deallocated memory") |
196 | 0 | .Case("heap-buffer-overflow", "Heap buffer overflow") |
197 | 0 | .Case("stack-buffer-underflow", "Stack buffer underflow") |
198 | 0 | .Case("initialization-order-fiasco", "Initialization order problem") |
199 | 0 | .Case("stack-buffer-overflow", "Stack buffer overflow") |
200 | 0 | .Case("stack-use-after-return", "Use of stack memory after return") |
201 | 0 | .Case("use-after-poison", "Use of poisoned memory") |
202 | 0 | .Case("container-overflow", "Container overflow") |
203 | 0 | .Case("stack-use-after-scope", "Use of out-of-scope stack memory") |
204 | 0 | .Case("global-buffer-overflow", "Global buffer overflow") |
205 | 0 | .Case("unknown-crash", "Invalid memory access") |
206 | 0 | .Case("stack-overflow", "Stack space exhausted") |
207 | 0 | .Case("null-deref", "Dereference of null pointer") |
208 | 0 | .Case("wild-jump", "Jump to non-executable address") |
209 | 0 | .Case("wild-addr-write", "Write through wild pointer") |
210 | 0 | .Case("wild-addr-read", "Read from wild pointer") |
211 | 0 | .Case("wild-addr", "Access through wild pointer") |
212 | 0 | .Case("signal", "Deadly signal") |
213 | 0 | .Case("double-free", "Deallocation of freed memory") |
214 | 0 | .Case("new-delete-type-mismatch", |
215 | 0 | "Deallocation size different from allocation size") |
216 | 0 | .Case("bad-free", "Deallocation of non-allocated memory") |
217 | 0 | .Case("alloc-dealloc-mismatch", |
218 | 0 | "Mismatch between allocation and deallocation APIs") |
219 | 0 | .Case("bad-malloc_usable_size", "Invalid argument to malloc_usable_size") |
220 | 0 | .Case("bad-__sanitizer_get_allocated_size", |
221 | 0 | "Invalid argument to __sanitizer_get_allocated_size") |
222 | 0 | .Case("param-overlap", |
223 | 0 | "Call to function disallowing overlapping memory ranges") |
224 | 0 | .Case("negative-size-param", "Negative size used when accessing memory") |
225 | 0 | .Case("bad-__sanitizer_annotate_contiguous_container", |
226 | 0 | "Invalid argument to __sanitizer_annotate_contiguous_container") |
227 | 0 | .Case("odr-violation", "Symbol defined in multiple translation units") |
228 | 0 | .Case( |
229 | 0 | "invalid-pointer-pair", |
230 | 0 | "Comparison or arithmetic on pointers from different memory regions") |
231 | | // for unknown report codes just show the code |
232 | 0 | .Default("AddressSanitizer detected: " + description); |
233 | 0 | } |
234 | | |
235 | | bool InstrumentationRuntimeASan::NotifyBreakpointHit( |
236 | | void *baton, StoppointCallbackContext *context, user_id_t break_id, |
237 | 0 | user_id_t break_loc_id) { |
238 | 0 | assert(baton && "null baton"); |
239 | 0 | if (!baton) |
240 | 0 | return false; |
241 | | |
242 | 0 | InstrumentationRuntimeASan *const instance = |
243 | 0 | static_cast<InstrumentationRuntimeASan *>(baton); |
244 | |
|
245 | 0 | ProcessSP process_sp = instance->GetProcessSP(); |
246 | |
|
247 | 0 | if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) |
248 | 0 | return false; |
249 | | |
250 | 0 | StructuredData::ObjectSP report = instance->RetrieveReportData(); |
251 | 0 | std::string description; |
252 | 0 | if (report) { |
253 | 0 | description = instance->FormatDescription(report); |
254 | 0 | } |
255 | | // Make sure this is the right process |
256 | 0 | if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { |
257 | 0 | ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); |
258 | 0 | if (thread_sp) |
259 | 0 | thread_sp->SetStopInfo(InstrumentationRuntimeStopInfo:: |
260 | 0 | CreateStopReasonWithInstrumentationData( |
261 | 0 | *thread_sp, description, report)); |
262 | |
|
263 | 0 | StreamFileSP stream_sp( |
264 | 0 | process_sp->GetTarget().GetDebugger().GetOutputStreamSP()); |
265 | 0 | if (stream_sp) { |
266 | 0 | stream_sp->Printf("AddressSanitizer report breakpoint hit. Use 'thread " |
267 | 0 | "info -s' to get extended information about the " |
268 | 0 | "report.\n"); |
269 | 0 | } |
270 | 0 | return true; // Return true to stop the target |
271 | 0 | } else |
272 | 0 | return false; // Let target run |
273 | 0 | } |
274 | | |
275 | 0 | void InstrumentationRuntimeASan::Activate() { |
276 | 0 | if (IsActive()) |
277 | 0 | return; |
278 | | |
279 | 0 | ProcessSP process_sp = GetProcessSP(); |
280 | 0 | if (!process_sp) |
281 | 0 | return; |
282 | | |
283 | 0 | ConstString symbol_name("_ZN6__asanL7AsanDieEv"); |
284 | 0 | const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType( |
285 | 0 | symbol_name, eSymbolTypeCode); |
286 | |
|
287 | 0 | if (symbol == nullptr) |
288 | 0 | return; |
289 | | |
290 | 0 | if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) |
291 | 0 | return; |
292 | | |
293 | 0 | Target &target = process_sp->GetTarget(); |
294 | 0 | addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); |
295 | |
|
296 | 0 | if (symbol_address == LLDB_INVALID_ADDRESS) |
297 | 0 | return; |
298 | | |
299 | 0 | const bool internal = true; |
300 | 0 | const bool hardware = false; |
301 | 0 | const bool sync = false; |
302 | 0 | Breakpoint *breakpoint = |
303 | 0 | process_sp->GetTarget() |
304 | 0 | .CreateBreakpoint(symbol_address, internal, hardware) |
305 | 0 | .get(); |
306 | 0 | breakpoint->SetCallback(InstrumentationRuntimeASan::NotifyBreakpointHit, this, |
307 | 0 | sync); |
308 | 0 | breakpoint->SetBreakpointKind("address-sanitizer-report"); |
309 | 0 | SetBreakpointID(breakpoint->GetID()); |
310 | |
|
311 | 0 | SetActive(true); |
312 | 0 | } |
313 | | |
314 | 2.05k | void InstrumentationRuntimeASan::Deactivate() { |
315 | 2.05k | if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) { |
316 | 0 | ProcessSP process_sp = GetProcessSP(); |
317 | 0 | if (process_sp) { |
318 | 0 | process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); |
319 | 0 | SetBreakpointID(LLDB_INVALID_BREAK_ID); |
320 | 0 | } |
321 | 0 | } |
322 | 2.05k | SetActive(false); |
323 | 2.05k | } |