LLDB  mainline
InstrumentationRuntimeUBSan.cpp
Go to the documentation of this file.
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 
10 
13 #include "lldb/Core/Debugger.h"
14 #include "lldb/Core/Module.h"
17 #include "lldb/Core/StreamFile.h"
18 #include "lldb/Core/ValueObject.h"
21 #include "lldb/Symbol/Symbol.h"
23 #include "lldb/Symbol/Variable.h"
27 #include "lldb/Target/StopInfo.h"
28 #include "lldb/Target/Target.h"
29 #include "lldb/Target/Thread.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 
40 
42 
43 lldb::InstrumentationRuntimeSP
44 InstrumentationRuntimeUBSan::CreateInstance(const lldb::ProcessSP &process_sp) {
45  return InstrumentationRuntimeSP(new InstrumentationRuntimeUBSan(process_sp));
46 }
47 
48 void InstrumentationRuntimeUBSan::Initialize() {
49  PluginManager::RegisterPlugin(
50  GetPluginNameStatic(),
51  "UndefinedBehaviorSanitizer instrumentation runtime plugin.",
52  CreateInstance, GetTypeStatic);
53 }
54 
55 void InstrumentationRuntimeUBSan::Terminate() {
56  PluginManager::UnregisterPlugin(CreateInstance);
57 }
58 
59 lldb::InstrumentationRuntimeType InstrumentationRuntimeUBSan::GetTypeStatic() {
61 }
62 
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 struct data {
72  const char *issue_kind;
73  const char *message;
74  const char *filename;
75  unsigned line;
76  unsigned col;
77  char *memory_addr;
78 };
79 )";
80 
82 data t;
83 __ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line,
84  &t.col, &t.memory_addr);
85 t;
86 )";
87 
88 static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp,
89  ProcessSP process_sp,
90  const std::string &expression_path) {
91  return return_value_sp->GetValueForExpressionPath(expression_path.c_str())
92  ->GetValueAsUnsigned(0);
93 }
94 
95 static std::string RetrieveString(ValueObjectSP return_value_sp,
96  ProcessSP process_sp,
97  const std::string &expression_path) {
98  addr_t ptr = RetrieveUnsigned(return_value_sp, process_sp, expression_path);
99  std::string str;
100  Status error;
101  process_sp->ReadCStringFromMemory(ptr, str, error);
102  return str;
103 }
104 
105 StructuredData::ObjectSP InstrumentationRuntimeUBSan::RetrieveReportData(
106  ExecutionContextRef exe_ctx_ref) {
107  ProcessSP process_sp = GetProcessSP();
108  if (!process_sp)
109  return StructuredData::ObjectSP();
110 
111  ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
112  StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
113  ModuleSP runtime_module_sp = GetRuntimeModuleSP();
114  Target &target = process_sp->GetTarget();
115 
116  if (!frame_sp)
117  return StructuredData::ObjectSP();
118 
119  StreamFileSP Stream = target.GetDebugger().GetOutputStreamSP();
120 
122  options.SetUnwindOnError(true);
123  options.SetTryAllThreads(true);
124  options.SetStopOthers(true);
125  options.SetIgnoreBreakpoints(true);
126  options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
128  options.SetAutoApplyFixIts(false);
130 
131  ValueObjectSP main_value;
132  ExecutionContext exe_ctx;
133  Status eval_error;
134  frame_sp->CalculateExecutionContext(exe_ctx);
135  ExpressionResults result = UserExpression::Evaluate(
136  exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "",
137  main_value, eval_error);
138  if (result != eExpressionCompleted) {
139  StreamString ss;
140  ss << "cannot evaluate UndefinedBehaviorSanitizer expression:\n";
141  ss << eval_error.AsCString();
142  Debugger::ReportWarning(ss.GetString().str(),
143  process_sp->GetTarget().GetDebugger().GetID());
144  return StructuredData::ObjectSP();
145  }
146 
147  // Gather the PCs of the user frames in the backtrace.
149  auto trace_sp = StructuredData::ObjectSP(trace);
150  for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
151  const Address FCA = thread_sp->GetStackFrameAtIndex(I)
152  ->GetFrameCodeAddressForSymbolication();
153  if (FCA.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
154  continue;
155 
156  lldb::addr_t PC = FCA.GetLoadAddress(&target);
158  }
159 
160  std::string IssueKind = RetrieveString(main_value, process_sp, ".issue_kind");
161  std::string ErrMessage = RetrieveString(main_value, process_sp, ".message");
162  std::string Filename = RetrieveString(main_value, process_sp, ".filename");
163  unsigned Line = RetrieveUnsigned(main_value, process_sp, ".line");
164  unsigned Col = RetrieveUnsigned(main_value, process_sp, ".col");
165  uintptr_t MemoryAddr =
166  RetrieveUnsigned(main_value, process_sp, ".memory_addr");
167 
168  auto *d = new StructuredData::Dictionary();
169  auto dict_sp = StructuredData::ObjectSP(d);
170  d->AddStringItem("instrumentation_class", "UndefinedBehaviorSanitizer");
171  d->AddStringItem("description", IssueKind);
172  d->AddStringItem("summary", ErrMessage);
173  d->AddStringItem("filename", Filename);
174  d->AddIntegerItem("line", Line);
175  d->AddIntegerItem("col", Col);
176  d->AddIntegerItem("memory_address", MemoryAddr);
177  d->AddIntegerItem("tid", thread_sp->GetID());
178  d->AddItem("trace", trace_sp);
179  return dict_sp;
180 }
181 
183  llvm::StringRef stop_reason_description_ref;
184  report->GetAsDictionary()->GetValueForKeyAsString(
185  "description", stop_reason_description_ref);
186  std::string stop_reason_description =
187  std::string(stop_reason_description_ref);
188 
189  if (!stop_reason_description.size()) {
190  stop_reason_description = "Undefined behavior detected";
191  } else {
192  stop_reason_description[0] = toupper(stop_reason_description[0]);
193  for (unsigned I = 1; I < stop_reason_description.size(); ++I)
194  if (stop_reason_description[I] == '-')
195  stop_reason_description[I] = ' ';
196  }
197  return stop_reason_description;
198 }
199 
200 bool InstrumentationRuntimeUBSan::NotifyBreakpointHit(
201  void *baton, StoppointCallbackContext *context, user_id_t break_id,
202  user_id_t break_loc_id) {
203  assert(baton && "null baton");
204  if (!baton)
205  return false; ///< false => resume execution.
206 
207  InstrumentationRuntimeUBSan *const instance =
208  static_cast<InstrumentationRuntimeUBSan *>(baton);
209 
210  ProcessSP process_sp = instance->GetProcessSP();
211  ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
212  if (!process_sp || !thread_sp ||
213  process_sp != context->exe_ctx_ref.GetProcessSP())
214  return false;
215 
216  if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
217  return false;
218 
219  StructuredData::ObjectSP report =
220  instance->RetrieveReportData(context->exe_ctx_ref);
221 
222  if (report) {
223  thread_sp->SetStopInfo(
224  InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
225  *thread_sp, GetStopReasonDescription(report), report));
226  return true;
227  }
228 
229  return false;
230 }
231 
232 const RegularExpression &
233 InstrumentationRuntimeUBSan::GetPatternForRuntimeLibrary() {
234  static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_"));
235  return regex;
236 }
237 
238 bool InstrumentationRuntimeUBSan::CheckIfRuntimeIsValid(
239  const lldb::ModuleSP module_sp) {
240  static ConstString ubsan_test_sym("__ubsan_on_report");
241  const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
242  ubsan_test_sym, lldb::eSymbolTypeAny);
243  return symbol != nullptr;
244 }
245 
246 // FIXME: Factor out all the logic we have in common with the {a,t}san plugins.
247 void InstrumentationRuntimeUBSan::Activate() {
248  if (IsActive())
249  return;
250 
251  ProcessSP process_sp = GetProcessSP();
252  if (!process_sp)
253  return;
254 
255  ModuleSP runtime_module_sp = GetRuntimeModuleSP();
256 
257  ConstString symbol_name("__ubsan_on_report");
258  const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
259  symbol_name, eSymbolTypeCode);
260 
261  if (symbol == nullptr)
262  return;
263 
264  if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
265  return;
266 
267  Target &target = process_sp->GetTarget();
268  addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
269 
270  if (symbol_address == LLDB_INVALID_ADDRESS)
271  return;
272 
273  Breakpoint *breakpoint =
274  process_sp->GetTarget()
275  .CreateBreakpoint(symbol_address, /*internal=*/true,
276  /*hardware=*/false)
277  .get();
278  const bool sync = false;
279  breakpoint->SetCallback(InstrumentationRuntimeUBSan::NotifyBreakpointHit,
280  this, sync);
281  breakpoint->SetBreakpointKind("undefined-behavior-sanitizer-report");
282  SetBreakpointID(breakpoint->GetID());
283 
284  SetActive(true);
285 }
286 
287 void InstrumentationRuntimeUBSan::Deactivate() {
288  SetActive(false);
289 
290  auto BID = GetBreakpointID();
291  if (BID == LLDB_INVALID_BREAK_ID)
292  return;
293 
294  if (ProcessSP process_sp = GetProcessSP()) {
295  process_sp->GetTarget().RemoveBreakpointByID(BID);
296  SetBreakpointID(LLDB_INVALID_BREAK_ID);
297  }
298 }
299 
300 lldb::ThreadCollectionSP
301 InstrumentationRuntimeUBSan::GetBacktracesFromExtendedStopInfo(
303  ThreadCollectionSP threads;
304  threads = std::make_shared<ThreadCollection>();
305 
306  ProcessSP process_sp = GetProcessSP();
307 
308  if (info->GetObjectForDotSeparatedPath("instrumentation_class")
309  ->GetStringValue() != "UndefinedBehaviorSanitizer")
310  return threads;
311 
312  std::vector<lldb::addr_t> PCs;
313  auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
314  trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
315  PCs.push_back(PC->GetAsInteger()->GetValue());
316  return true;
317  });
318 
319  if (PCs.empty())
320  return threads;
321 
322  StructuredData::ObjectSP thread_id_obj =
323  info->GetObjectForDotSeparatedPath("tid");
324  tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
325 
326  // We gather symbolication addresses above, so no need for HistoryThread to
327  // try to infer the call addresses.
328  bool pcs_are_call_addresses = true;
329  ThreadSP new_thread_sp = std::make_shared<HistoryThread>(
330  *process_sp, tid, PCs, pcs_are_call_addresses);
331  std::string stop_reason_description = GetStopReasonDescription(info);
332  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  process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
337  threads->AddThread(new_thread_sp);
338 
339  return threads;
340 }
RegularExpression.h
lldb_private::ExecutionContext
Definition: ExecutionContext.h:292
lldb_private::StructuredData::Dictionary
Definition: StructuredData.h:368
RetrieveString
static std::string RetrieveString(ValueObjectSP return_value_sp, ProcessSP process_sp, const std::string &expression_path)
Definition: InstrumentationRuntimeUBSan.cpp:95
InstrumentationRuntimeUBSan.h
lldb_private::RegularExpression
Definition: RegularExpression.h:18
lldb_private::StoppointCallbackContext
General Outline: When we hit a breakpoint we need to package up whatever information is needed to eva...
Definition: StoppointCallbackContext.h:26
lldb_private::Stoppoint::GetID
lldb::break_id_t GetID() const
Definition: Stoppoint.cpp:22
lldb_private::Address::IsValid
bool IsValid() const
Check if the object state is valid.
Definition: Address.h:345
lldb::eSymbolTypeCode
@ eSymbolTypeCode
Definition: lldb-enumerations.h:623
lldb_private::EvaluateExpressionOptions::SetTimeout
void SetTimeout(const Timeout< std::micro > &timeout)
Definition: Target.h:345
lldb_private::EvaluateExpressionOptions::SetLanguage
void SetLanguage(lldb::LanguageType language)
Definition: Target.h:305
lldb_private::StructuredData::Array
Definition: StructuredData.h:171
lldb_private::Symbol
Definition: Symbol.h:20
lldb_private::Symbol::ValueIsAddress
bool ValueIsAddress() const
Definition: Symbol.cpp:117
lldb_private::StructuredData::Integer
Definition: StructuredData.h:293
lldb_private::ExecutionContextRef::GetThreadSP
lldb::ThreadSP GetThreadSP() const
Get accessor that creates a strong reference from the weak thread reference contained in this object.
Definition: ExecutionContext.cpp:574
lldb_private::Breakpoint::SetBreakpointKind
void SetBreakpointKind(const char *kind)
Set the "kind" description for a breakpoint.
Definition: Breakpoint.h:453
lldb::ExpressionResults
ExpressionResults
The results of expression evaluation.
Definition: lldb-enumerations.h:271
Module.h
lldb_private::StoppointCallbackContext::exe_ctx_ref
ExecutionContextRef exe_ctx_ref
Definition: StoppointCallbackContext.h:43
lldb_private::EvaluateExpressionOptions
Definition: Target.h:277
StoppointCallbackContext.h
SectionLoadList.h
UserExpression.h
HistoryThread.h
lldb_private::Debugger::GetOutputStreamSP
lldb::StreamFileSP GetOutputStreamSP()
Definition: Debugger.h:137
lldb_private::Stream
Definition: Stream.h:28
lldb_private::Target::CreateBreakpoint
lldb::BreakpointSP CreateBreakpoint(const FileSpecList *containingModules, const FileSpec &file, uint32_t line_no, uint32_t column, lldb::addr_t offset, LazyBool check_inlines, LazyBool skip_prologue, bool internal, bool request_hardware, LazyBool move_to_nearest_code)
Definition: Target.cpp:353
lldb::addr_t
uint64_t addr_t
Definition: lldb-types.h:83
lldb_private::Target::GetDebugger
Debugger & GetDebugger()
Definition: Target.h:1030
CommandReturnObject.h
lldb_private::StructuredData::Object::GetAsInteger
Integer * GetAsInteger()
Definition: StructuredData.h:97
Debugger.h
lldb_private::Target
Definition: Target.h:469
lldb_private::Address::GetOpcodeLoadAddress
lldb::addr_t GetOpcodeLoadAddress(Target *target, AddressClass addr_class=AddressClass::eInvalid) const
Get the load address as an opcode load address.
Definition: Address.cpp:368
lldb_private::StreamString::GetString
llvm::StringRef GetString() const
Definition: StreamString.cpp:51
lldb_private::InstrumentationRuntimeUBSan
Definition: InstrumentationRuntimeUBSan.h:19
lldb_private::StructuredData::Integer::GetValue
uint64_t GetValue()
Definition: StructuredData.h:302
Target.h
error
static llvm::raw_ostream & error(Stream &strm)
Definition: CommandReturnObject.cpp:17
lldb::eSymbolTypeAny
@ eSymbolTypeAny
Definition: lldb-enumerations.h:620
lldb::eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer
@ eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer
Definition: lldb-enumerations.h:489
InstrumentationRuntimeStopInfo.h
lldb_private::StructuredData::ObjectSP
std::shared_ptr< Object > ObjectSP
Definition: StructuredData.h:59
lldb::eExpressionCompleted
@ eExpressionCompleted
Definition: lldb-enumerations.h:272
lldb_private::Symbol::GetAddressRef
Address & GetAddressRef()
Definition: Symbol.h:57
lldb_private::ConstString
Definition: ConstString.h:39
lldb_private::StreamString
Definition: StreamString.h:23
lldb_private::EvaluateExpressionOptions::SetTryAllThreads
void SetTryAllThreads(bool try_others=true)
Definition: Target.h:357
lldb_private::StructuredData::Array::AddItem
void AddItem(const ObjectSP &item)
Definition: StructuredData.h:282
PluginInterface.h
RetrieveUnsigned
static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp, ProcessSP process_sp, const std::string &expression_path)
Definition: InstrumentationRuntimeUBSan.cpp:88
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb_private::EvaluateExpressionOptions::SetUnwindOnError
void SetUnwindOnError(bool unwind=false)
Definition: Target.h:324
Thread.h
lldb_private::InstrumentationRuntimeUBSan::RetrieveReportData
StructuredData::ObjectSP RetrieveReportData(ExecutionContextRef exe_ctx_ref)
Definition: InstrumentationRuntimeUBSan.cpp:105
VariableList.h
ValueObject.h
LLDB_INVALID_BREAK_ID
#define LLDB_INVALID_BREAK_ID
Definition: lldb-defines.h:37
lldb_private::EvaluateExpressionOptions::SetIgnoreBreakpoints
void SetIgnoreBreakpoints(bool ignore=false)
Definition: Target.h:328
Symbol.h
StreamFile.h
lldb_private::Status
Definition: Status.h:44
lldb_private::Address
Definition: Address.h:59
lldb_private::Breakpoint::SetCallback
void SetCallback(BreakpointHitCallback callback, void *baton, bool is_synchronous=false)
Set the callback action invoked when the breakpoint is hit.
Definition: Breakpoint.cpp:418
lldb_private::Address::GetLoadAddress
lldb::addr_t GetLoadAddress(Target *target) const
Get the load address.
Definition: Address.cpp:311
ub_sanitizer_retrieve_report_data_command
static const char * ub_sanitizer_retrieve_report_data_command
Definition: InstrumentationRuntimeUBSan.cpp:81
StopInfo.h
PluginManager.h
LLDB_INVALID_ADDRESS
#define LLDB_INVALID_ADDRESS
Definition: lldb-defines.h:74
lldb::user_id_t
uint64_t user_id_t
Definition: lldb-types.h:84
SymbolContext.h
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
ub_sanitizer_retrieve_report_data_prefix
static const char * ub_sanitizer_retrieve_report_data_prefix
Definition: InstrumentationRuntimeUBSan.cpp:63
lldb_private::Breakpoint::GetTarget
Target & GetTarget()
Accessor for the breakpoint Target.
Definition: Breakpoint.h:464
lldb_private::InstrumentationRuntime::GetProcessSP
lldb::ProcessSP GetProcessSP()
Definition: InstrumentationRuntime.h:50
lldb_private::Address::GetModule
lldb::ModuleSP GetModule() const
Get accessor for the module for this address.
Definition: Address.cpp:283
Stream.h
LLDB_PLUGIN_DEFINE
#define LLDB_PLUGIN_DEFINE(PluginName)
Definition: PluginManager.h:31
lldb_private::EvaluateExpressionOptions::SetStopOthers
void SetStopOthers(bool stop_others=true)
Definition: Target.h:361
lldb_private::ExecutionContextRef::GetProcessSP
lldb::ProcessSP GetProcessSP() const
Get accessor that creates a strong reference from the weak process reference contained in this object...
Definition: ExecutionContext.cpp:567
lldb::eLanguageTypeObjC_plus_plus
@ eLanguageTypeObjC_plus_plus
Objective-C++.
Definition: lldb-enumerations.h:455
Variable.h
lldb_private::EvaluateExpressionOptions::SetPrefix
void SetPrefix(const char *prefix)
Definition: Target.h:313
lldb_private::StructuredData::Object
Definition: StructuredData.h:70
lldb
Definition: SBAddress.h:15
lldb_private::ExecutionContextRef
Execution context objects refer to objects in the execution of the program that is being debugged.
Definition: ExecutionContext.h:72
GetStopReasonDescription
static std::string GetStopReasonDescription(StructuredData::ObjectSP report)
Definition: InstrumentationRuntimeUBSan.cpp:182
lldb_private::Breakpoint
General Outline: A breakpoint has four main parts, a filter, a resolver, the list of breakpoint locat...
Definition: Breakpoint.h:80
lldb::tid_t
uint64_t tid_t
Definition: lldb-types.h:86
lldb::InstrumentationRuntimeType
InstrumentationRuntimeType
Definition: lldb-enumerations.h:486
lldb_private::Status::AsCString
const char * AsCString(const char *default_error_str="unknown error") const
Get the error string associated with the current error.
Definition: Status.cpp:130
lldb_private::EvaluateExpressionOptions::SetAutoApplyFixIts
void SetAutoApplyFixIts(bool b)
Definition: Target.h:420