1320384Sdim//===-- UBSanRuntime.cpp ----------------------------------------*- C++ -*-===//
2320384Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6320384Sdim//
7320384Sdim//===----------------------------------------------------------------------===//
8320384Sdim
9320384Sdim#include "UBSanRuntime.h"
10320384Sdim
11320384Sdim#include "Plugins/Process/Utility/HistoryThread.h"
12320384Sdim#include "lldb/Breakpoint/StoppointCallbackContext.h"
13320384Sdim#include "lldb/Core/Debugger.h"
14320384Sdim#include "lldb/Core/Module.h"
15320384Sdim#include "lldb/Core/PluginInterface.h"
16320384Sdim#include "lldb/Core/PluginManager.h"
17320384Sdim#include "lldb/Core/StreamFile.h"
18320384Sdim#include "lldb/Core/ValueObject.h"
19320384Sdim#include "lldb/Expression/UserExpression.h"
20320384Sdim#include "lldb/Interpreter/CommandReturnObject.h"
21320384Sdim#include "lldb/Symbol/Symbol.h"
22320384Sdim#include "lldb/Symbol/SymbolContext.h"
23320384Sdim#include "lldb/Symbol/Variable.h"
24320384Sdim#include "lldb/Symbol/VariableList.h"
25320384Sdim#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
26320384Sdim#include "lldb/Target/SectionLoadList.h"
27320384Sdim#include "lldb/Target/StopInfo.h"
28320384Sdim#include "lldb/Target/Target.h"
29320384Sdim#include "lldb/Target/Thread.h"
30320384Sdim#include "lldb/Utility/RegularExpression.h"
31320384Sdim#include "lldb/Utility/Stream.h"
32320384Sdim#include <ctype.h>
33320384Sdim
34353358Sdim#include <memory>
35353358Sdim
36320384Sdimusing namespace lldb;
37320384Sdimusing namespace lldb_private;
38320384Sdim
39320384SdimUndefinedBehaviorSanitizerRuntime::~UndefinedBehaviorSanitizerRuntime() {
40320384Sdim  Deactivate();
41320384Sdim}
42320384Sdim
43320384Sdimlldb::InstrumentationRuntimeSP
44320384SdimUndefinedBehaviorSanitizerRuntime::CreateInstance(
45320384Sdim    const lldb::ProcessSP &process_sp) {
46320384Sdim  return InstrumentationRuntimeSP(
47320384Sdim      new UndefinedBehaviorSanitizerRuntime(process_sp));
48320384Sdim}
49320384Sdim
50320384Sdimvoid UndefinedBehaviorSanitizerRuntime::Initialize() {
51320384Sdim  PluginManager::RegisterPlugin(
52320384Sdim      GetPluginNameStatic(),
53320384Sdim      "UndefinedBehaviorSanitizer instrumentation runtime plugin.",
54320384Sdim      CreateInstance, GetTypeStatic);
55320384Sdim}
56320384Sdim
57320384Sdimvoid UndefinedBehaviorSanitizerRuntime::Terminate() {
58320384Sdim  PluginManager::UnregisterPlugin(CreateInstance);
59320384Sdim}
60320384Sdim
61320384Sdimlldb_private::ConstString
62320384SdimUndefinedBehaviorSanitizerRuntime::GetPluginNameStatic() {
63320384Sdim  return ConstString("UndefinedBehaviorSanitizer");
64320384Sdim}
65320384Sdim
66320384Sdimlldb::InstrumentationRuntimeType
67320384SdimUndefinedBehaviorSanitizerRuntime::GetTypeStatic() {
68320384Sdim  return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer;
69320384Sdim}
70320384Sdim
71320384Sdimstatic const char *ub_sanitizer_retrieve_report_data_prefix = R"(
72320384Sdimextern "C" {
73320384Sdimvoid
74320384Sdim__ubsan_get_current_report_data(const char **OutIssueKind,
75320384Sdim    const char **OutMessage, const char **OutFilename, unsigned *OutLine,
76320384Sdim    unsigned *OutCol, char **OutMemoryAddr);
77320384Sdim}
78320384Sdim
79320384Sdimstruct data {
80320384Sdim  const char *issue_kind;
81320384Sdim  const char *message;
82320384Sdim  const char *filename;
83320384Sdim  unsigned line;
84320384Sdim  unsigned col;
85320384Sdim  char *memory_addr;
86320384Sdim};
87320384Sdim)";
88320384Sdim
89320384Sdimstatic const char *ub_sanitizer_retrieve_report_data_command = R"(
90320384Sdimdata t;
91320384Sdim__ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line,
92320384Sdim                                &t.col, &t.memory_addr);
93320384Sdimt;
94320384Sdim)";
95320384Sdim
96320384Sdimstatic addr_t RetrieveUnsigned(ValueObjectSP return_value_sp,
97320384Sdim                               ProcessSP process_sp,
98320384Sdim                               const std::string &expression_path) {
99320384Sdim  return return_value_sp->GetValueForExpressionPath(expression_path.c_str())
100320384Sdim      ->GetValueAsUnsigned(0);
101320384Sdim}
102320384Sdim
103320384Sdimstatic std::string RetrieveString(ValueObjectSP return_value_sp,
104320384Sdim                                  ProcessSP process_sp,
105320384Sdim                                  const std::string &expression_path) {
106320384Sdim  addr_t ptr = RetrieveUnsigned(return_value_sp, process_sp, expression_path);
107320384Sdim  std::string str;
108320384Sdim  Status error;
109320384Sdim  process_sp->ReadCStringFromMemory(ptr, str, error);
110320384Sdim  return str;
111320384Sdim}
112320384Sdim
113320384SdimStructuredData::ObjectSP UndefinedBehaviorSanitizerRuntime::RetrieveReportData(
114320384Sdim    ExecutionContextRef exe_ctx_ref) {
115320384Sdim  ProcessSP process_sp = GetProcessSP();
116320384Sdim  if (!process_sp)
117320384Sdim    return StructuredData::ObjectSP();
118320384Sdim
119320384Sdim  ThreadSP thread_sp = exe_ctx_ref.GetThreadSP();
120320384Sdim  StackFrameSP frame_sp = thread_sp->GetSelectedFrame();
121320384Sdim  ModuleSP runtime_module_sp = GetRuntimeModuleSP();
122320384Sdim  Target &target = process_sp->GetTarget();
123320384Sdim
124320384Sdim  if (!frame_sp)
125320384Sdim    return StructuredData::ObjectSP();
126320384Sdim
127360784Sdim  StreamFileSP Stream = target.GetDebugger().GetOutputStreamSP();
128320384Sdim
129320384Sdim  EvaluateExpressionOptions options;
130320384Sdim  options.SetUnwindOnError(true);
131320384Sdim  options.SetTryAllThreads(true);
132320384Sdim  options.SetStopOthers(true);
133320384Sdim  options.SetIgnoreBreakpoints(true);
134353358Sdim  options.SetTimeout(process_sp->GetUtilityExpressionTimeout());
135320384Sdim  options.SetPrefix(ub_sanitizer_retrieve_report_data_prefix);
136320384Sdim  options.SetAutoApplyFixIts(false);
137320384Sdim  options.SetLanguage(eLanguageTypeObjC_plus_plus);
138320384Sdim
139320384Sdim  ValueObjectSP main_value;
140320384Sdim  ExecutionContext exe_ctx;
141320384Sdim  Status eval_error;
142320384Sdim  frame_sp->CalculateExecutionContext(exe_ctx);
143320384Sdim  ExpressionResults result = UserExpression::Evaluate(
144320384Sdim      exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "",
145320384Sdim      main_value, eval_error);
146320384Sdim  if (result != eExpressionCompleted) {
147320384Sdim    target.GetDebugger().GetAsyncOutputStream()->Printf(
148320384Sdim        "Warning: Cannot evaluate UndefinedBehaviorSanitizer expression:\n%s\n",
149320384Sdim        eval_error.AsCString());
150320384Sdim    return StructuredData::ObjectSP();
151320384Sdim  }
152320384Sdim
153320384Sdim  // Gather the PCs of the user frames in the backtrace.
154320384Sdim  StructuredData::Array *trace = new StructuredData::Array();
155320384Sdim  auto trace_sp = StructuredData::ObjectSP(trace);
156320384Sdim  for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) {
157320384Sdim    const Address FCA =
158320384Sdim        thread_sp->GetStackFrameAtIndex(I)->GetFrameCodeAddress();
159320384Sdim    if (FCA.GetModule() == runtime_module_sp) // Skip PCs from the runtime.
160320384Sdim      continue;
161320384Sdim
162320384Sdim    lldb::addr_t PC = FCA.GetLoadAddress(&target);
163320384Sdim    trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC)));
164320384Sdim  }
165320384Sdim
166320384Sdim  std::string IssueKind = RetrieveString(main_value, process_sp, ".issue_kind");
167320384Sdim  std::string ErrMessage = RetrieveString(main_value, process_sp, ".message");
168320384Sdim  std::string Filename = RetrieveString(main_value, process_sp, ".filename");
169320384Sdim  unsigned Line = RetrieveUnsigned(main_value, process_sp, ".line");
170320384Sdim  unsigned Col = RetrieveUnsigned(main_value, process_sp, ".col");
171320384Sdim  uintptr_t MemoryAddr =
172320384Sdim      RetrieveUnsigned(main_value, process_sp, ".memory_addr");
173320384Sdim
174320384Sdim  auto *d = new StructuredData::Dictionary();
175320384Sdim  auto dict_sp = StructuredData::ObjectSP(d);
176320384Sdim  d->AddStringItem("instrumentation_class", "UndefinedBehaviorSanitizer");
177320384Sdim  d->AddStringItem("description", IssueKind);
178320384Sdim  d->AddStringItem("summary", ErrMessage);
179320384Sdim  d->AddStringItem("filename", Filename);
180320384Sdim  d->AddIntegerItem("line", Line);
181320384Sdim  d->AddIntegerItem("col", Col);
182320384Sdim  d->AddIntegerItem("memory_address", MemoryAddr);
183320384Sdim  d->AddIntegerItem("tid", thread_sp->GetID());
184320384Sdim  d->AddItem("trace", trace_sp);
185320384Sdim  return dict_sp;
186320384Sdim}
187320384Sdim
188320384Sdimstatic std::string GetStopReasonDescription(StructuredData::ObjectSP report) {
189320384Sdim  llvm::StringRef stop_reason_description_ref;
190320384Sdim  report->GetAsDictionary()->GetValueForKeyAsString("description",
191320384Sdim                                                    stop_reason_description_ref);
192320384Sdim  std::string stop_reason_description = stop_reason_description_ref;
193320384Sdim
194320384Sdim  if (!stop_reason_description.size()) {
195320384Sdim    stop_reason_description = "Undefined behavior detected";
196320384Sdim  } else {
197320384Sdim    stop_reason_description[0] = toupper(stop_reason_description[0]);
198320384Sdim    for (unsigned I = 1; I < stop_reason_description.size(); ++I)
199320384Sdim      if (stop_reason_description[I] == '-')
200320384Sdim        stop_reason_description[I] = ' ';
201320384Sdim  }
202320384Sdim  return stop_reason_description;
203320384Sdim}
204320384Sdim
205320384Sdimbool UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit(
206320384Sdim    void *baton, StoppointCallbackContext *context, user_id_t break_id,
207320384Sdim    user_id_t break_loc_id) {
208320384Sdim  assert(baton && "null baton");
209320384Sdim  if (!baton)
210360784Sdim    return false; ///< false => resume execution.
211320384Sdim
212320384Sdim  UndefinedBehaviorSanitizerRuntime *const instance =
213320384Sdim      static_cast<UndefinedBehaviorSanitizerRuntime *>(baton);
214320384Sdim
215320384Sdim  ProcessSP process_sp = instance->GetProcessSP();
216320384Sdim  ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP();
217320384Sdim  if (!process_sp || !thread_sp ||
218320384Sdim      process_sp != context->exe_ctx_ref.GetProcessSP())
219320384Sdim    return false;
220320384Sdim
221320970Sdim  if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
222320970Sdim    return false;
223320970Sdim
224320384Sdim  StructuredData::ObjectSP report =
225320384Sdim      instance->RetrieveReportData(context->exe_ctx_ref);
226320384Sdim
227320384Sdim  if (report) {
228320384Sdim    thread_sp->SetStopInfo(
229320384Sdim        InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
230320384Sdim            *thread_sp, GetStopReasonDescription(report), report));
231320384Sdim    return true;
232320384Sdim  }
233320384Sdim
234320384Sdim  return false;
235320384Sdim}
236320384Sdim
237320384Sdimconst RegularExpression &
238320384SdimUndefinedBehaviorSanitizerRuntime::GetPatternForRuntimeLibrary() {
239320384Sdim  static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_"));
240320384Sdim  return regex;
241320384Sdim}
242320384Sdim
243320384Sdimbool UndefinedBehaviorSanitizerRuntime::CheckIfRuntimeIsValid(
244320384Sdim    const lldb::ModuleSP module_sp) {
245320384Sdim  static ConstString ubsan_test_sym("__ubsan_on_report");
246320384Sdim  const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType(
247320384Sdim      ubsan_test_sym, lldb::eSymbolTypeAny);
248320384Sdim  return symbol != nullptr;
249320384Sdim}
250320384Sdim
251320384Sdim// FIXME: Factor out all the logic we have in common with the {a,t}san plugins.
252320384Sdimvoid UndefinedBehaviorSanitizerRuntime::Activate() {
253320384Sdim  if (IsActive())
254320384Sdim    return;
255320384Sdim
256320384Sdim  ProcessSP process_sp = GetProcessSP();
257320384Sdim  if (!process_sp)
258320384Sdim    return;
259320384Sdim
260320384Sdim  ModuleSP runtime_module_sp = GetRuntimeModuleSP();
261320384Sdim
262320384Sdim  ConstString symbol_name("__ubsan_on_report");
263320384Sdim  const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType(
264320384Sdim      symbol_name, eSymbolTypeCode);
265320384Sdim
266320384Sdim  if (symbol == nullptr)
267320384Sdim    return;
268320384Sdim
269320384Sdim  if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid())
270320384Sdim    return;
271320384Sdim
272320384Sdim  Target &target = process_sp->GetTarget();
273320384Sdim  addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target);
274320384Sdim
275320384Sdim  if (symbol_address == LLDB_INVALID_ADDRESS)
276320384Sdim    return;
277320384Sdim
278320384Sdim  Breakpoint *breakpoint =
279320384Sdim      process_sp->GetTarget()
280320384Sdim          .CreateBreakpoint(symbol_address, /*internal=*/true,
281320384Sdim                            /*hardware=*/false)
282320384Sdim          .get();
283320384Sdim  breakpoint->SetCallback(
284320384Sdim      UndefinedBehaviorSanitizerRuntime::NotifyBreakpointHit, this, true);
285320384Sdim  breakpoint->SetBreakpointKind("undefined-behavior-sanitizer-report");
286320384Sdim  SetBreakpointID(breakpoint->GetID());
287320384Sdim
288320384Sdim  SetActive(true);
289320384Sdim}
290320384Sdim
291320384Sdimvoid UndefinedBehaviorSanitizerRuntime::Deactivate() {
292320384Sdim  SetActive(false);
293320384Sdim
294320384Sdim  auto BID = GetBreakpointID();
295320384Sdim  if (BID == LLDB_INVALID_BREAK_ID)
296320384Sdim    return;
297320384Sdim
298320384Sdim  if (ProcessSP process_sp = GetProcessSP()) {
299320384Sdim    process_sp->GetTarget().RemoveBreakpointByID(BID);
300320384Sdim    SetBreakpointID(LLDB_INVALID_BREAK_ID);
301320384Sdim  }
302320384Sdim}
303320384Sdim
304320384Sdimlldb::ThreadCollectionSP
305320384SdimUndefinedBehaviorSanitizerRuntime::GetBacktracesFromExtendedStopInfo(
306320384Sdim    StructuredData::ObjectSP info) {
307320384Sdim  ThreadCollectionSP threads;
308353358Sdim  threads = std::make_shared<ThreadCollection>();
309320384Sdim
310320384Sdim  ProcessSP process_sp = GetProcessSP();
311320384Sdim
312320384Sdim  if (info->GetObjectForDotSeparatedPath("instrumentation_class")
313320384Sdim          ->GetStringValue() != "UndefinedBehaviorSanitizer")
314320384Sdim    return threads;
315320384Sdim
316320384Sdim  std::vector<lldb::addr_t> PCs;
317320384Sdim  auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray();
318320384Sdim  trace->ForEach([&PCs](StructuredData::Object *PC) -> bool {
319320384Sdim    PCs.push_back(PC->GetAsInteger()->GetValue());
320320384Sdim    return true;
321320384Sdim  });
322320384Sdim
323320384Sdim  if (PCs.empty())
324320384Sdim    return threads;
325320384Sdim
326320384Sdim  StructuredData::ObjectSP thread_id_obj =
327320384Sdim      info->GetObjectForDotSeparatedPath("tid");
328320384Sdim  tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0;
329320384Sdim
330353358Sdim  HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs);
331320384Sdim  ThreadSP new_thread_sp(history_thread);
332320384Sdim  std::string stop_reason_description = GetStopReasonDescription(info);
333320384Sdim  new_thread_sp->SetName(stop_reason_description.c_str());
334320384Sdim
335341825Sdim  // Save this in the Process' ExtendedThreadList so a strong pointer retains
336341825Sdim  // the object
337320384Sdim  process_sp->GetExtendedThreadList().AddThread(new_thread_sp);
338320384Sdim  threads->AddThread(new_thread_sp);
339320384Sdim
340320384Sdim  return threads;
341320384Sdim}
342