1 //===-- InstrumentationRuntimeUBSan.cpp -----------------------------------===// 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 "InstrumentationRuntimeUBSan.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 36 using namespace lldb; 37 using namespace lldb_private; 38 39 LLDB_PLUGIN_DEFINE(InstrumentationRuntimeUBSan) 40 41 InstrumentationRuntimeUBSan::~InstrumentationRuntimeUBSan() { Deactivate(); } 42 43 lldb::InstrumentationRuntimeSP 44 InstrumentationRuntimeUBSan::CreateInstance(const lldb::ProcessSP &process_sp) { 45 return InstrumentationRuntimeSP(new InstrumentationRuntimeUBSan(process_sp)); 46 } 47 48 void InstrumentationRuntimeUBSan::Initialize() { 49 PluginManager::RegisterPlugin( 50 GetPluginNameStatic(), 51 "UndefinedBehaviorSanitizer instrumentation runtime plugin.", 52 CreateInstance, GetTypeStatic); 53 } 54 55 void InstrumentationRuntimeUBSan::Terminate() { 56 PluginManager::UnregisterPlugin(CreateInstance); 57 } 58 59 lldb_private::ConstString InstrumentationRuntimeUBSan::GetPluginNameStatic() { 60 return ConstString("UndefinedBehaviorSanitizer"); 61 } 62 63 lldb::InstrumentationRuntimeType InstrumentationRuntimeUBSan::GetTypeStatic() { 64 return eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer; 65 } 66 67 static const char *ub_sanitizer_retrieve_report_data_prefix = R"( 68 extern "C" { 69 void 70 __ubsan_get_current_report_data(const char **OutIssueKind, 71 const char **OutMessage, const char **OutFilename, unsigned *OutLine, 72 unsigned *OutCol, char **OutMemoryAddr); 73 } 74 75 struct data { 76 const char *issue_kind; 77 const char *message; 78 const char *filename; 79 unsigned line; 80 unsigned col; 81 char *memory_addr; 82 }; 83 )"; 84 85 static const char *ub_sanitizer_retrieve_report_data_command = R"( 86 data t; 87 __ubsan_get_current_report_data(&t.issue_kind, &t.message, &t.filename, &t.line, 88 &t.col, &t.memory_addr); 89 t; 90 )"; 91 92 static addr_t RetrieveUnsigned(ValueObjectSP return_value_sp, 93 ProcessSP process_sp, 94 const std::string &expression_path) { 95 return return_value_sp->GetValueForExpressionPath(expression_path.c_str()) 96 ->GetValueAsUnsigned(0); 97 } 98 99 static std::string RetrieveString(ValueObjectSP return_value_sp, 100 ProcessSP process_sp, 101 const std::string &expression_path) { 102 addr_t ptr = RetrieveUnsigned(return_value_sp, process_sp, expression_path); 103 std::string str; 104 Status error; 105 process_sp->ReadCStringFromMemory(ptr, str, error); 106 return str; 107 } 108 109 StructuredData::ObjectSP InstrumentationRuntimeUBSan::RetrieveReportData( 110 ExecutionContextRef exe_ctx_ref) { 111 ProcessSP process_sp = GetProcessSP(); 112 if (!process_sp) 113 return StructuredData::ObjectSP(); 114 115 ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); 116 StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); 117 ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 118 Target &target = process_sp->GetTarget(); 119 120 if (!frame_sp) 121 return StructuredData::ObjectSP(); 122 123 StreamFileSP Stream = target.GetDebugger().GetOutputStreamSP(); 124 125 EvaluateExpressionOptions options; 126 options.SetUnwindOnError(true); 127 options.SetTryAllThreads(true); 128 options.SetStopOthers(true); 129 options.SetIgnoreBreakpoints(true); 130 options.SetTimeout(process_sp->GetUtilityExpressionTimeout()); 131 options.SetPrefix(ub_sanitizer_retrieve_report_data_prefix); 132 options.SetAutoApplyFixIts(false); 133 options.SetLanguage(eLanguageTypeObjC_plus_plus); 134 135 ValueObjectSP main_value; 136 ExecutionContext exe_ctx; 137 Status eval_error; 138 frame_sp->CalculateExecutionContext(exe_ctx); 139 ExpressionResults result = UserExpression::Evaluate( 140 exe_ctx, options, ub_sanitizer_retrieve_report_data_command, "", 141 main_value, eval_error); 142 if (result != eExpressionCompleted) { 143 target.GetDebugger().GetAsyncOutputStream()->Printf( 144 "Warning: Cannot evaluate UndefinedBehaviorSanitizer expression:\n%s\n", 145 eval_error.AsCString()); 146 return StructuredData::ObjectSP(); 147 } 148 149 // Gather the PCs of the user frames in the backtrace. 150 StructuredData::Array *trace = new StructuredData::Array(); 151 auto trace_sp = StructuredData::ObjectSP(trace); 152 for (unsigned I = 0; I < thread_sp->GetStackFrameCount(); ++I) { 153 const Address FCA = 154 thread_sp->GetStackFrameAtIndex(I)->GetFrameCodeAddress(); 155 if (FCA.GetModule() == runtime_module_sp) // Skip PCs from the runtime. 156 continue; 157 158 lldb::addr_t PC = FCA.GetLoadAddress(&target); 159 trace->AddItem(StructuredData::ObjectSP(new StructuredData::Integer(PC))); 160 } 161 162 std::string IssueKind = RetrieveString(main_value, process_sp, ".issue_kind"); 163 std::string ErrMessage = RetrieveString(main_value, process_sp, ".message"); 164 std::string Filename = RetrieveString(main_value, process_sp, ".filename"); 165 unsigned Line = RetrieveUnsigned(main_value, process_sp, ".line"); 166 unsigned Col = RetrieveUnsigned(main_value, process_sp, ".col"); 167 uintptr_t MemoryAddr = 168 RetrieveUnsigned(main_value, process_sp, ".memory_addr"); 169 170 auto *d = new StructuredData::Dictionary(); 171 auto dict_sp = StructuredData::ObjectSP(d); 172 d->AddStringItem("instrumentation_class", "UndefinedBehaviorSanitizer"); 173 d->AddStringItem("description", IssueKind); 174 d->AddStringItem("summary", ErrMessage); 175 d->AddStringItem("filename", Filename); 176 d->AddIntegerItem("line", Line); 177 d->AddIntegerItem("col", Col); 178 d->AddIntegerItem("memory_address", MemoryAddr); 179 d->AddIntegerItem("tid", thread_sp->GetID()); 180 d->AddItem("trace", trace_sp); 181 return dict_sp; 182 } 183 184 static std::string GetStopReasonDescription(StructuredData::ObjectSP report) { 185 llvm::StringRef stop_reason_description_ref; 186 report->GetAsDictionary()->GetValueForKeyAsString( 187 "description", stop_reason_description_ref); 188 std::string stop_reason_description = 189 std::string(stop_reason_description_ref); 190 191 if (!stop_reason_description.size()) { 192 stop_reason_description = "Undefined behavior detected"; 193 } else { 194 stop_reason_description[0] = toupper(stop_reason_description[0]); 195 for (unsigned I = 1; I < stop_reason_description.size(); ++I) 196 if (stop_reason_description[I] == '-') 197 stop_reason_description[I] = ' '; 198 } 199 return stop_reason_description; 200 } 201 202 bool InstrumentationRuntimeUBSan::NotifyBreakpointHit( 203 void *baton, StoppointCallbackContext *context, user_id_t break_id, 204 user_id_t break_loc_id) { 205 assert(baton && "null baton"); 206 if (!baton) 207 return false; ///< false => resume execution. 208 209 InstrumentationRuntimeUBSan *const instance = 210 static_cast<InstrumentationRuntimeUBSan *>(baton); 211 212 ProcessSP process_sp = instance->GetProcessSP(); 213 ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); 214 if (!process_sp || !thread_sp || 215 process_sp != context->exe_ctx_ref.GetProcessSP()) 216 return false; 217 218 if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) 219 return false; 220 221 StructuredData::ObjectSP report = 222 instance->RetrieveReportData(context->exe_ctx_ref); 223 224 if (report) { 225 thread_sp->SetStopInfo( 226 InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData( 227 *thread_sp, GetStopReasonDescription(report), report)); 228 return true; 229 } 230 231 return false; 232 } 233 234 const RegularExpression & 235 InstrumentationRuntimeUBSan::GetPatternForRuntimeLibrary() { 236 static RegularExpression regex(llvm::StringRef("libclang_rt\\.(a|t|ub)san_")); 237 return regex; 238 } 239 240 bool InstrumentationRuntimeUBSan::CheckIfRuntimeIsValid( 241 const lldb::ModuleSP module_sp) { 242 static ConstString ubsan_test_sym("__ubsan_on_report"); 243 const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( 244 ubsan_test_sym, lldb::eSymbolTypeAny); 245 return symbol != nullptr; 246 } 247 248 // FIXME: Factor out all the logic we have in common with the {a,t}san plugins. 249 void InstrumentationRuntimeUBSan::Activate() { 250 if (IsActive()) 251 return; 252 253 ProcessSP process_sp = GetProcessSP(); 254 if (!process_sp) 255 return; 256 257 ModuleSP runtime_module_sp = GetRuntimeModuleSP(); 258 259 ConstString symbol_name("__ubsan_on_report"); 260 const Symbol *symbol = runtime_module_sp->FindFirstSymbolWithNameAndType( 261 symbol_name, eSymbolTypeCode); 262 263 if (symbol == nullptr) 264 return; 265 266 if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) 267 return; 268 269 Target &target = process_sp->GetTarget(); 270 addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); 271 272 if (symbol_address == LLDB_INVALID_ADDRESS) 273 return; 274 275 Breakpoint *breakpoint = 276 process_sp->GetTarget() 277 .CreateBreakpoint(symbol_address, /*internal=*/true, 278 /*hardware=*/false) 279 .get(); 280 breakpoint->SetCallback(InstrumentationRuntimeUBSan::NotifyBreakpointHit, 281 this, true); 282 breakpoint->SetBreakpointKind("undefined-behavior-sanitizer-report"); 283 SetBreakpointID(breakpoint->GetID()); 284 285 SetActive(true); 286 } 287 288 void InstrumentationRuntimeUBSan::Deactivate() { 289 SetActive(false); 290 291 auto BID = GetBreakpointID(); 292 if (BID == LLDB_INVALID_BREAK_ID) 293 return; 294 295 if (ProcessSP process_sp = GetProcessSP()) { 296 process_sp->GetTarget().RemoveBreakpointByID(BID); 297 SetBreakpointID(LLDB_INVALID_BREAK_ID); 298 } 299 } 300 301 lldb::ThreadCollectionSP 302 InstrumentationRuntimeUBSan::GetBacktracesFromExtendedStopInfo( 303 StructuredData::ObjectSP info) { 304 ThreadCollectionSP threads; 305 threads = std::make_shared<ThreadCollection>(); 306 307 ProcessSP process_sp = GetProcessSP(); 308 309 if (info->GetObjectForDotSeparatedPath("instrumentation_class") 310 ->GetStringValue() != "UndefinedBehaviorSanitizer") 311 return threads; 312 313 std::vector<lldb::addr_t> PCs; 314 auto trace = info->GetObjectForDotSeparatedPath("trace")->GetAsArray(); 315 trace->ForEach([&PCs](StructuredData::Object *PC) -> bool { 316 PCs.push_back(PC->GetAsInteger()->GetValue()); 317 return true; 318 }); 319 320 if (PCs.empty()) 321 return threads; 322 323 StructuredData::ObjectSP thread_id_obj = 324 info->GetObjectForDotSeparatedPath("tid"); 325 tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; 326 327 HistoryThread *history_thread = new HistoryThread(*process_sp, tid, PCs); 328 ThreadSP new_thread_sp(history_thread); 329 std::string stop_reason_description = GetStopReasonDescription(info); 330 new_thread_sp->SetName(stop_reason_description.c_str()); 331 332 // Save this in the Process' ExtendedThreadList so a strong pointer retains 333 // the object 334 process_sp->GetExtendedThreadList().AddThread(new_thread_sp); 335 threads->AddThread(new_thread_sp); 336 337 return threads; 338 } 339