UBSanRuntime.cpp revision 321369
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
35using namespace lldb;
36using namespace lldb_private;
37
38UndefinedBehaviorSanitizerRuntime::~UndefinedBehaviorSanitizerRuntime() {
39  Deactivate();
40}
41
42lldb::InstrumentationRuntimeSP
43UndefinedBehaviorSanitizerRuntime::CreateInstance(
44    const lldb::ProcessSP &process_sp) {
45  return InstrumentationRuntimeSP(
46      new UndefinedBehaviorSanitizerRuntime(process_sp));
47}
48
49void UndefinedBehaviorSanitizerRuntime::Initialize() {
50  PluginManager::RegisterPlugin(
51      GetPluginNameStatic(),
52      "UndefinedBehaviorSanitizer instrumentation runtime plugin.",
53      CreateInstance, GetTypeStatic);
54}
55
56void UndefinedBehaviorSanitizerRuntime::Terminate() {
57  PluginManager::UnregisterPlugin(CreateInstance);
58}
59
60lldb_private::ConstString
61UndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() {
62  return ConstString("UndefinedBehaviorSanitizer");
63}
64
65lldb::InstrumentationRuntimeType
66UndefinedBehaviorSanitizerRuntime::GetTypeStatic() {
67  return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer;
68}
69
70static const char *ub_sanitizer_retrieve_report_data_prefix = R"(
71extern "C" {
72void
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
78struct 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
88static const char *ub_sanitizer_retrieve_report_data_command = R"(
89data t;
90__ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line,
91                                &t.col, &t.memory_addr);
92t;
93)";
94
95static 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
102static 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
112StructuredData::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
187static 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
204bool 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
236const RegularExpression &
237UndefinedBehaviorSanitizerRuntime::GetPatternForRuntimeLibrary() {
238  static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_"));
239  return regex;
240}
241
242bool 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.
251void 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
290void 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
303lldb::ThreadCollectionSP
304UndefinedBehaviorSanitizerRuntime::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
338  // retains the object
339  process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
340  threads->AddThread(new_thread_sp);
341
342  return threads;
343}
344