LLDB  mainline
UBSanRuntime.cpp
Go to the documentation of this file.
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 
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 <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 
62 UndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() {
63  return ConstString("UndefinedBehaviorSanitizer");
64 }
65 
67 UndefinedBehaviorSanitizerRuntime::GetTypeStatic() {
69 }
70 
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 
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 
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.
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);
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 
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(
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  uint32_t stop_id = 0;
331  bool stop_id_is_valid = false;
332  HistoryThread *history_thread =
333  new HistoryThread(*process_sp, tid, PCs, stop_id, stop_id_is_valid);
334  ThreadSP new_thread_sp(history_thread);
335  std::string stop_reason_description = GetStopReasonDescription(info);
336  new_thread_sp->SetName(stop_reason_description.c_str());
337 
338  // Save this in the Process' ExtendedThreadList so a strong pointer retains
339  // the object
340  process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
341  threads->AddThread(new_thread_sp);
342 
343  return threads;
344 }
Address & GetAddressRef()
Definition: Symbol.h:56
Enumerations for broadcasting.
Definition: SBLaunchInfo.h:14
A stream class that can stream formatted output to a file.
Definition: Stream.h:28
lldb::addr_t GetLoadAddress(Target *target) const
Get the load address.
Definition: Address.cpp:292
General Outline: A breakpoint has four main parts, a filter, a resolver, the list of breakpoint locat...
Definition: Breakpoint.h:78
void SetUnwindOnError(bool unwind=false)
Definition: Target.h:285
lldb::ProcessSP GetProcessSP() const
Get accessor that creates a strong reference from the weak process reference contained in this object...
"lldb/Utility/RegularExpression.h" A C++ wrapper class for regex.
lldb::StreamFileSP GetOutputFile()
Definition: Debugger.h:131
void SetBreakpointKind(const char *kind)
Set the "kind" description for a breakpoint.
Definition: Breakpoint.h:456
Target & GetTarget()
Accessor for the breakpoint Target.
Definition: Breakpoint.h:467
"lldb/Target/ExecutionContext.h" A class that contains an execution context.
static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp, ProcessSP process_sp, const std::string &expression_path)
static const char * ub_sanitizer_retrieve_report_data_command
static std::string RetrieveString(ValueObjectSP return_value_sp, ProcessSP process_sp, const std::string &expression_path)
void SetCallback(BreakpointHitCallback callback, void *baton, bool is_synchronous=false)
Set the callback action invoked when the breakpoint is hit.
Definition: Breakpoint.cpp:421
lldb::ThreadSP GetThreadSP() const
Get accessor that creates a strong reference from the weak thread reference contained in this object...
#define LLDB_INVALID_ADDRESS
Invalid value definitions.
Definition: lldb-defines.h:85
lldb::StreamSP GetAsyncOutputStream()
Definition: Debugger.cpp:1149
uint64_t user_id_t
Definition: lldb-types.h:84
InstrumentationRuntimeType
static const char * ub_sanitizer_retrieve_report_data_prefix
lldb::break_id_t GetID() const
Definition: Stoppoint.cpp:22
uint64_t tid_t
Definition: lldb-types.h:86
lldb::addr_t GetOpcodeLoadAddress(Target *target, AddressClass addr_class=AddressClass::eInvalid) const
Get the load address as an opcode load address.
Definition: Address.cpp:349
static std::string GetStopReasonDescription(StructuredData::ObjectSP report)
bool ValueIsAddress() const
Definition: Symbol.cpp:114
Execution context objects refer to objects in the execution of the program that is being debugged...
bool IsValid() const
Check if the object state is valid.
Definition: Address.h:343
A section + offset based address class.
Definition: Address.h:80
A thread object representing a backtrace from a previous point in the process execution.
Definition: HistoryThread.h:34
uint64_t addr_t
Definition: lldb-types.h:83
A uniqued constant string class.
Definition: ConstString.h:38
lldb::ModuleSP GetModule() const
Get accessor for the module for this address.
Definition: Address.cpp:264
Definition: SBAddress.h:15
std::shared_ptr< Object > ObjectSP
#define LLDB_INVALID_BREAK_ID
Definition: lldb-defines.h:49
General Outline: When we hit a breakpoint we need to package up whatever information is needed to eva...
const char * AsCString(const char *default_error_str="unknown error") const
Get the error string associated with the current error.
Definition: Status.cpp:130
Debugger & GetDebugger()
Definition: Target.h:974
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:325
An error handling class.
Definition: Status.h:44