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
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/StreamFile.h"
18#include "lldb/Core/ValueObject.h"
19#include "lldb/Expression/UserExpression.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 <ctype.h>
33
34#include <memory>
35
36using namespace lldb;
37using namespace lldb_private;
38
39UndefinedBehaviorSanitizerRuntime::~UndefinedBehaviorSanitizerRuntime() {
40  Deactivate();
41}
42
43lldb::InstrumentationRuntimeSP
44UndefinedBehaviorSanitizerRuntime::CreateInstance(
45    const lldb::ProcessSP &process_sp) {
46  return InstrumentationRuntimeSP(
47      new UndefinedBehaviorSanitizerRuntime(process_sp));
48}
49
50void UndefinedBehaviorSanitizerRuntime::Initialize() {
51  PluginManager::RegisterPlugin(
52      GetPluginNameStatic(),
53      "UndefinedBehaviorSanitizer instrumentation runtime plugin.",
54      CreateInstance, GetTypeStatic);
55}
56
57void UndefinedBehaviorSanitizerRuntime::Terminate() {
58  PluginManager::UnregisterPlugin(CreateInstance);
59}
60
61lldb_private::ConstString
62UndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() {
63  return ConstString("UndefinedBehaviorSanitizer");
64}
65
66lldb::InstrumentationRuntimeType
67UndefinedBehaviorSanitizerRuntime::GetTypeStatic() {
68  return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer;
69}
70
71static const char *ub_sanitizer_retrieve_report_data_prefix = R"(
72extern "C" {
73void
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
79struct 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
89static const char *ub_sanitizer_retrieve_report_data_command = R"(
90data t;
91__ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line,
92                                &t.col, &t.memory_addr);
93t;
94)";
95
96static 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
103static 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
113StructuredData::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().GetOutputStreamSP();
128
129  EvaluateExpressionOptions options;
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.
154  StructuredData::Array *trace = new StructuredData::Array();
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);
163    trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
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
188static std::string GetStopReasonDescription(StructuredData::ObjectSP report) {
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
205bool 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
237const RegularExpression &
238UndefinedBehaviorSanitizerRuntime::GetPatternForRuntimeLibrary() {
239  static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_"));
240  return regex;
241}
242
243bool 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.
252void 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
291void 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
304lldb::ThreadCollectionSP
305UndefinedBehaviorSanitizerRuntime::GetBacktracesFromExtendedStopInfo(
306    StructuredData::ObjectSP info) {
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  HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs);
331  ThreadSP new_thread_sp(history_thread);
332  std::string stop_reason_description = GetStopReasonDescription(info);
333  new_thread_sp->SetName(stop_reason_description.c_str());
334
335  // Save this in the Process' ExtendedThreadList so a strong pointer retains
336  // the object
337  process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
338  threads->AddThread(new_thread_sp);
339
340  return threads;
341}
342