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