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