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