1 //===-- ubsan_diag.cc -----------------------------------------------------===// 2 // 3 // This file is distributed under the University of Illinois Open Source 4 // License. See LICENSE.TXT for details. 5 // 6 //===----------------------------------------------------------------------===// 7 // 8 // Diagnostic reporting for the UBSan runtime. 9 // 10 //===----------------------------------------------------------------------===// 11 12 #include "ubsan_platform.h" 13 #if CAN_SANITIZE_UB 14 #include "ubsan_diag.h" 15 #include "ubsan_init.h" 16 #include "ubsan_flags.h" 17 #include "sanitizer_common/sanitizer_placement_new.h" 18 #include "sanitizer_common/sanitizer_report_decorator.h" 19 #include "sanitizer_common/sanitizer_stacktrace.h" 20 #include "sanitizer_common/sanitizer_stacktrace_printer.h" 21 #include "sanitizer_common/sanitizer_suppressions.h" 22 #include "sanitizer_common/sanitizer_symbolizer.h" 23 #include <stdio.h> 24 25 using namespace __ubsan; 26 27 static void MaybePrintStackTrace(uptr pc, uptr bp) { 28 // We assume that flags are already parsed, as UBSan runtime 29 // will definitely be called when we print the first diagnostics message. 30 if (!flags()->print_stacktrace) 31 return; 32 // We can only use slow unwind, as we don't have any information about stack 33 // top/bottom. 34 // FIXME: It's better to respect "fast_unwind_on_fatal" runtime flag and 35 // fetch stack top/bottom information if we have it (e.g. if we're running 36 // under ASan). 37 if (StackTrace::WillUseFastUnwind(false)) 38 return; 39 BufferedStackTrace stack; 40 stack.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false); 41 stack.Print(); 42 } 43 44 static const char *ConvertTypeToString(ErrorType Type) { 45 switch (Type) { 46 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \ 47 case ErrorType::Name: \ 48 return SummaryKind; 49 #include "ubsan_checks.inc" 50 #undef UBSAN_CHECK 51 } 52 UNREACHABLE("unknown ErrorType!"); 53 } 54 55 static const char *ConvertTypeToFlagName(ErrorType Type) { 56 switch (Type) { 57 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \ 58 case ErrorType::Name: \ 59 return FSanitizeFlagName; 60 #include "ubsan_checks.inc" 61 #undef UBSAN_CHECK 62 } 63 UNREACHABLE("unknown ErrorType!"); 64 } 65 66 static void MaybeReportErrorSummary(Location Loc, ErrorType Type) { 67 if (!common_flags()->print_summary) 68 return; 69 if (!flags()->report_error_type) 70 Type = ErrorType::GenericUB; 71 const char *ErrorKind = ConvertTypeToString(Type); 72 if (Loc.isSourceLocation()) { 73 SourceLocation SLoc = Loc.getSourceLocation(); 74 if (!SLoc.isInvalid()) { 75 AddressInfo AI; 76 AI.file = internal_strdup(SLoc.getFilename()); 77 AI.line = SLoc.getLine(); 78 AI.column = SLoc.getColumn(); 79 AI.function = internal_strdup(""); // Avoid printing ?? as function name. 80 ReportErrorSummary(ErrorKind, AI); 81 AI.Clear(); 82 return; 83 } 84 } else if (Loc.isSymbolizedStack()) { 85 const AddressInfo &AI = Loc.getSymbolizedStack()->info; 86 ReportErrorSummary(ErrorKind, AI); 87 return; 88 } 89 ReportErrorSummary(ErrorKind); 90 } 91 92 namespace { 93 class Decorator : public SanitizerCommonDecorator { 94 public: 95 Decorator() : SanitizerCommonDecorator() {} 96 const char *Highlight() const { return Green(); } 97 const char *EndHighlight() const { return Default(); } 98 const char *Note() const { return Black(); } 99 const char *EndNote() const { return Default(); } 100 }; 101 } 102 103 SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) { 104 InitAsStandaloneIfNecessary(); 105 return Symbolizer::GetOrInit()->SymbolizePC(PC); 106 } 107 108 Diag &Diag::operator<<(const TypeDescriptor &V) { 109 return AddArg(V.getTypeName()); 110 } 111 112 Diag &Diag::operator<<(const Value &V) { 113 if (V.getType().isSignedIntegerTy()) 114 AddArg(V.getSIntValue()); 115 else if (V.getType().isUnsignedIntegerTy()) 116 AddArg(V.getUIntValue()); 117 else if (V.getType().isFloatTy()) 118 AddArg(V.getFloatValue()); 119 else 120 AddArg("<unknown>"); 121 return *this; 122 } 123 124 /// Hexadecimal printing for numbers too large for Printf to handle directly. 125 static void RenderHex(InternalScopedString *Buffer, UIntMax Val) { 126 #if HAVE_INT128_T 127 Buffer->append("0x%08x%08x%08x%08x", (unsigned int)(Val >> 96), 128 (unsigned int)(Val >> 64), (unsigned int)(Val >> 32), 129 (unsigned int)(Val)); 130 #else 131 UNREACHABLE("long long smaller than 64 bits?"); 132 #endif 133 } 134 135 static void RenderLocation(InternalScopedString *Buffer, Location Loc) { 136 switch (Loc.getKind()) { 137 case Location::LK_Source: { 138 SourceLocation SLoc = Loc.getSourceLocation(); 139 if (SLoc.isInvalid()) 140 Buffer->append("<unknown>"); 141 else 142 RenderSourceLocation(Buffer, SLoc.getFilename(), SLoc.getLine(), 143 SLoc.getColumn(), common_flags()->symbolize_vs_style, 144 common_flags()->strip_path_prefix); 145 return; 146 } 147 case Location::LK_Memory: 148 Buffer->append("%p", Loc.getMemoryLocation()); 149 return; 150 case Location::LK_Symbolized: { 151 const AddressInfo &Info = Loc.getSymbolizedStack()->info; 152 if (Info.file) 153 RenderSourceLocation(Buffer, Info.file, Info.line, Info.column, 154 common_flags()->symbolize_vs_style, 155 common_flags()->strip_path_prefix); 156 else if (Info.module) 157 RenderModuleLocation(Buffer, Info.module, Info.module_offset, 158 common_flags()->strip_path_prefix); 159 else 160 Buffer->append("%p", Info.address); 161 return; 162 } 163 case Location::LK_Null: 164 Buffer->append("<unknown>"); 165 return; 166 } 167 } 168 169 static void RenderText(InternalScopedString *Buffer, const char *Message, 170 const Diag::Arg *Args) { 171 for (const char *Msg = Message; *Msg; ++Msg) { 172 if (*Msg != '%') { 173 Buffer->append("%c", *Msg); 174 continue; 175 } 176 const Diag::Arg &A = Args[*++Msg - '0']; 177 switch (A.Kind) { 178 case Diag::AK_String: 179 Buffer->append("%s", A.String); 180 break; 181 case Diag::AK_TypeName: { 182 if (SANITIZER_WINDOWS) 183 // The Windows implementation demangles names early. 184 Buffer->append("'%s'", A.String); 185 else 186 Buffer->append("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); 187 break; 188 } 189 case Diag::AK_SInt: 190 // 'long long' is guaranteed to be at least 64 bits wide. 191 if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX) 192 Buffer->append("%lld", (long long)A.SInt); 193 else 194 RenderHex(Buffer, A.SInt); 195 break; 196 case Diag::AK_UInt: 197 if (A.UInt <= UINT64_MAX) 198 Buffer->append("%llu", (unsigned long long)A.UInt); 199 else 200 RenderHex(Buffer, A.UInt); 201 break; 202 case Diag::AK_Float: { 203 // FIXME: Support floating-point formatting in sanitizer_common's 204 // printf, and stop using snprintf here. 205 char FloatBuffer[32]; 206 #if SANITIZER_WINDOWS 207 sprintf_s(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float); 208 #else 209 snprintf(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float); 210 #endif 211 Buffer->append("%s", FloatBuffer); 212 break; 213 } 214 case Diag::AK_Pointer: 215 Buffer->append("%p", A.Pointer); 216 break; 217 } 218 } 219 } 220 221 /// Find the earliest-starting range in Ranges which ends after Loc. 222 static Range *upperBound(MemoryLocation Loc, Range *Ranges, 223 unsigned NumRanges) { 224 Range *Best = 0; 225 for (unsigned I = 0; I != NumRanges; ++I) 226 if (Ranges[I].getEnd().getMemoryLocation() > Loc && 227 (!Best || 228 Best->getStart().getMemoryLocation() > 229 Ranges[I].getStart().getMemoryLocation())) 230 Best = &Ranges[I]; 231 return Best; 232 } 233 234 static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) { 235 return (LHS < RHS) ? 0 : LHS - RHS; 236 } 237 238 static inline uptr addNoOverflow(uptr LHS, uptr RHS) { 239 const uptr Limit = (uptr)-1; 240 return (LHS > Limit - RHS) ? Limit : LHS + RHS; 241 } 242 243 /// Render a snippet of the address space near a location. 244 static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc, 245 Range *Ranges, unsigned NumRanges, 246 const Diag::Arg *Args) { 247 // Show at least the 8 bytes surrounding Loc. 248 const unsigned MinBytesNearLoc = 4; 249 MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc); 250 MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc); 251 MemoryLocation OrigMin = Min; 252 for (unsigned I = 0; I < NumRanges; ++I) { 253 Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min); 254 Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max); 255 } 256 257 // If we have too many interesting bytes, prefer to show bytes after Loc. 258 const unsigned BytesToShow = 32; 259 if (Max - Min > BytesToShow) 260 Min = __sanitizer::Min(Max - BytesToShow, OrigMin); 261 Max = addNoOverflow(Min, BytesToShow); 262 263 if (!IsAccessibleMemoryRange(Min, Max - Min)) { 264 Printf("<memory cannot be printed>\n"); 265 return; 266 } 267 268 // Emit data. 269 InternalScopedString Buffer(1024); 270 for (uptr P = Min; P != Max; ++P) { 271 unsigned char C = *reinterpret_cast<const unsigned char*>(P); 272 Buffer.append("%s%02x", (P % 8 == 0) ? " " : " ", C); 273 } 274 Buffer.append("\n"); 275 276 // Emit highlights. 277 Buffer.append(Decor.Highlight()); 278 Range *InRange = upperBound(Min, Ranges, NumRanges); 279 for (uptr P = Min; P != Max; ++P) { 280 char Pad = ' ', Byte = ' '; 281 if (InRange && InRange->getEnd().getMemoryLocation() == P) 282 InRange = upperBound(P, Ranges, NumRanges); 283 if (!InRange && P > Loc) 284 break; 285 if (InRange && InRange->getStart().getMemoryLocation() < P) 286 Pad = '~'; 287 if (InRange && InRange->getStart().getMemoryLocation() <= P) 288 Byte = '~'; 289 if (P % 8 == 0) 290 Buffer.append("%c", Pad); 291 Buffer.append("%c", Pad); 292 Buffer.append("%c", P == Loc ? '^' : Byte); 293 Buffer.append("%c", Byte); 294 } 295 Buffer.append("%s\n", Decor.EndHighlight()); 296 297 // Go over the line again, and print names for the ranges. 298 InRange = 0; 299 unsigned Spaces = 0; 300 for (uptr P = Min; P != Max; ++P) { 301 if (!InRange || InRange->getEnd().getMemoryLocation() == P) 302 InRange = upperBound(P, Ranges, NumRanges); 303 if (!InRange) 304 break; 305 306 Spaces += (P % 8) == 0 ? 2 : 1; 307 308 if (InRange && InRange->getStart().getMemoryLocation() == P) { 309 while (Spaces--) 310 Buffer.append(" "); 311 RenderText(&Buffer, InRange->getText(), Args); 312 Buffer.append("\n"); 313 // FIXME: We only support naming one range for now! 314 break; 315 } 316 317 Spaces += 2; 318 } 319 320 Printf("%s", Buffer.data()); 321 // FIXME: Print names for anything we can identify within the line: 322 // 323 // * If we can identify the memory itself as belonging to a particular 324 // global, stack variable, or dynamic allocation, then do so. 325 // 326 // * If we have a pointer-size, pointer-aligned range highlighted, 327 // determine whether the value of that range is a pointer to an 328 // entity which we can name, and if so, print that name. 329 // 330 // This needs an external symbolizer, or (preferably) ASan instrumentation. 331 } 332 333 Diag::~Diag() { 334 // All diagnostics should be printed under report mutex. 335 CommonSanitizerReportMutex.CheckLocked(); 336 Decorator Decor; 337 InternalScopedString Buffer(1024); 338 339 Buffer.append(Decor.Bold()); 340 RenderLocation(&Buffer, Loc); 341 Buffer.append(":"); 342 343 switch (Level) { 344 case DL_Error: 345 Buffer.append("%s runtime error: %s%s", Decor.Warning(), Decor.EndWarning(), 346 Decor.Bold()); 347 break; 348 349 case DL_Note: 350 Buffer.append("%s note: %s", Decor.Note(), Decor.EndNote()); 351 break; 352 } 353 354 RenderText(&Buffer, Message, Args); 355 356 Buffer.append("%s\n", Decor.Default()); 357 Printf("%s", Buffer.data()); 358 359 if (Loc.isMemoryLocation()) 360 PrintMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, NumRanges, Args); 361 } 362 363 ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc, 364 ErrorType Type) 365 : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) { 366 InitAsStandaloneIfNecessary(); 367 CommonSanitizerReportMutex.Lock(); 368 } 369 370 ScopedReport::~ScopedReport() { 371 MaybePrintStackTrace(Opts.pc, Opts.bp); 372 MaybeReportErrorSummary(SummaryLoc, Type); 373 CommonSanitizerReportMutex.Unlock(); 374 if (flags()->halt_on_error) 375 Die(); 376 } 377 378 ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; 379 static SuppressionContext *suppression_ctx = nullptr; 380 static const char kVptrCheck[] = "vptr_check"; 381 static const char *kSuppressionTypes[] = { 382 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName, 383 #include "ubsan_checks.inc" 384 #undef UBSAN_CHECK 385 kVptrCheck, 386 }; 387 388 void __ubsan::InitializeSuppressions() { 389 CHECK_EQ(nullptr, suppression_ctx); 390 suppression_ctx = new (suppression_placeholder) // NOLINT 391 SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); 392 suppression_ctx->ParseFromFile(flags()->suppressions); 393 } 394 395 bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) { 396 InitAsStandaloneIfNecessary(); 397 CHECK(suppression_ctx); 398 Suppression *s; 399 return suppression_ctx->Match(TypeName, kVptrCheck, &s); 400 } 401 402 bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) { 403 InitAsStandaloneIfNecessary(); 404 CHECK(suppression_ctx); 405 const char *SuppType = ConvertTypeToFlagName(ET); 406 // Fast path: don't symbolize PC if there is no suppressions for given UB 407 // type. 408 if (!suppression_ctx->HasSuppressionType(SuppType)) 409 return false; 410 Suppression *s = nullptr; 411 // Suppress by file name known to runtime. 412 if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s)) 413 return true; 414 // Suppress by module name. 415 if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) { 416 if (suppression_ctx->Match(Module, SuppType, &s)) 417 return true; 418 } 419 // Suppress by function or source file name from debug info. 420 SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC)); 421 const AddressInfo &AI = Stack.get()->info; 422 return suppression_ctx->Match(AI.function, SuppType, &s) || 423 suppression_ctx->Match(AI.file, SuppType, &s); 424 } 425 426 #endif // CAN_SANITIZE_UB 427