ubsan_diag.cc revision 5d71de26cedae3dafc17449fe0182045c0bd20e8
1//===-- ubsan_diag.cc -----------------------------------------------------===// 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// Diagnostic reporting for the UBSan runtime. 11// 12//===----------------------------------------------------------------------===// 13 14#include "ubsan_diag.h" 15#include "sanitizer_common/sanitizer_common.h" 16#include "sanitizer_common/sanitizer_flags.h" 17#include "sanitizer_common/sanitizer_libc.h" 18#include "sanitizer_common/sanitizer_report_decorator.h" 19#include "sanitizer_common/sanitizer_stacktrace.h" 20#include "sanitizer_common/sanitizer_symbolizer.h" 21#include <stdio.h> 22 23using namespace __ubsan; 24 25static void InitializeSanitizerCommon() { 26 static StaticSpinMutex init_mu; 27 SpinMutexLock l(&init_mu); 28 static bool initialized; 29 if (initialized) 30 return; 31 if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) { 32 // UBSan is run in a standalone mode. Initialize it now. 33 SanitizerToolName = "UndefinedBehaviorSanitizer"; 34 CommonFlags *cf = common_flags(); 35 SetCommonFlagsDefaults(cf); 36 cf->print_summary = false; 37 } 38 initialized = true; 39} 40 41namespace { 42class Decorator : public SanitizerCommonDecorator { 43 public: 44 Decorator() : SanitizerCommonDecorator() {} 45 const char *Highlight() const { return Green(); } 46 const char *EndHighlight() const { return Default(); } 47 const char *Note() const { return Black(); } 48 const char *EndNote() const { return Default(); } 49}; 50} 51 52Location __ubsan::getCallerLocation(uptr CallerLoc) { 53 if (!CallerLoc) 54 return Location(); 55 56 uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); 57 return getFunctionLocation(Loc, 0); 58} 59 60Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) { 61 if (!Loc) 62 return Location(); 63 InitializeSanitizerCommon(); 64 65 AddressInfo Info; 66 if (!Symbolizer::GetOrInit()->SymbolizePC(Loc, &Info, 1) || 67 !Info.module || !*Info.module) 68 return Location(Loc); 69 70 if (FName && Info.function) 71 *FName = Info.function; 72 73 if (!Info.file) 74 return ModuleLocation(Info.module, Info.module_offset); 75 76 return SourceLocation(Info.file, Info.line, Info.column); 77} 78 79Diag &Diag::operator<<(const TypeDescriptor &V) { 80 return AddArg(V.getTypeName()); 81} 82 83Diag &Diag::operator<<(const Value &V) { 84 if (V.getType().isSignedIntegerTy()) 85 AddArg(V.getSIntValue()); 86 else if (V.getType().isUnsignedIntegerTy()) 87 AddArg(V.getUIntValue()); 88 else if (V.getType().isFloatTy()) 89 AddArg(V.getFloatValue()); 90 else 91 AddArg("<unknown>"); 92 return *this; 93} 94 95/// Hexadecimal printing for numbers too large for Printf to handle directly. 96static void PrintHex(UIntMax Val) { 97#if HAVE_INT128_T 98 Printf("0x%08x%08x%08x%08x", 99 (unsigned int)(Val >> 96), 100 (unsigned int)(Val >> 64), 101 (unsigned int)(Val >> 32), 102 (unsigned int)(Val)); 103#else 104 UNREACHABLE("long long smaller than 64 bits?"); 105#endif 106} 107 108static void renderLocation(Location Loc) { 109 InternalScopedString LocBuffer(1024); 110 switch (Loc.getKind()) { 111 case Location::LK_Source: { 112 SourceLocation SLoc = Loc.getSourceLocation(); 113 if (SLoc.isInvalid()) 114 LocBuffer.append("<unknown>"); 115 else 116 PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), 117 SLoc.getColumn()); 118 break; 119 } 120 case Location::LK_Module: 121 PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(), 122 Loc.getModuleLocation().getOffset()); 123 break; 124 case Location::LK_Memory: 125 LocBuffer.append("%p", Loc.getMemoryLocation()); 126 break; 127 case Location::LK_Null: 128 LocBuffer.append("<unknown>"); 129 break; 130 } 131 Printf("%s:", LocBuffer.data()); 132} 133 134static void renderText(const char *Message, const Diag::Arg *Args) { 135 for (const char *Msg = Message; *Msg; ++Msg) { 136 if (*Msg != '%') { 137 char Buffer[64]; 138 unsigned I; 139 for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I) 140 Buffer[I] = Msg[I]; 141 Buffer[I] = '\0'; 142 Printf(Buffer); 143 Msg += I - 1; 144 } else { 145 const Diag::Arg &A = Args[*++Msg - '0']; 146 switch (A.Kind) { 147 case Diag::AK_String: 148 Printf("%s", A.String); 149 break; 150 case Diag::AK_Mangled: { 151 Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); 152 break; 153 } 154 case Diag::AK_SInt: 155 // 'long long' is guaranteed to be at least 64 bits wide. 156 if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX) 157 Printf("%lld", (long long)A.SInt); 158 else 159 PrintHex(A.SInt); 160 break; 161 case Diag::AK_UInt: 162 if (A.UInt <= UINT64_MAX) 163 Printf("%llu", (unsigned long long)A.UInt); 164 else 165 PrintHex(A.UInt); 166 break; 167 case Diag::AK_Float: { 168 // FIXME: Support floating-point formatting in sanitizer_common's 169 // printf, and stop using snprintf here. 170 char Buffer[32]; 171 snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float); 172 Printf("%s", Buffer); 173 break; 174 } 175 case Diag::AK_Pointer: 176 Printf("%p", A.Pointer); 177 break; 178 } 179 } 180 } 181} 182 183/// Find the earliest-starting range in Ranges which ends after Loc. 184static Range *upperBound(MemoryLocation Loc, Range *Ranges, 185 unsigned NumRanges) { 186 Range *Best = 0; 187 for (unsigned I = 0; I != NumRanges; ++I) 188 if (Ranges[I].getEnd().getMemoryLocation() > Loc && 189 (!Best || 190 Best->getStart().getMemoryLocation() > 191 Ranges[I].getStart().getMemoryLocation())) 192 Best = &Ranges[I]; 193 return Best; 194} 195 196/// Render a snippet of the address space near a location. 197static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc, 198 Range *Ranges, unsigned NumRanges, 199 const Diag::Arg *Args) { 200 const unsigned BytesToShow = 32; 201 const unsigned MinBytesNearLoc = 4; 202 203 // Show at least the 8 bytes surrounding Loc. 204 MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc; 205 for (unsigned I = 0; I < NumRanges; ++I) { 206 Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min); 207 Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max); 208 } 209 210 // If we have too many interesting bytes, prefer to show bytes after Loc. 211 if (Max - Min > BytesToShow) 212 Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc); 213 Max = Min + BytesToShow; 214 215 // Emit data. 216 for (uptr P = Min; P != Max; ++P) { 217 // FIXME: Check that the address is readable before printing it. 218 unsigned char C = *reinterpret_cast<const unsigned char*>(P); 219 Printf("%s%02x", (P % 8 == 0) ? " " : " ", C); 220 } 221 Printf("\n"); 222 223 // Emit highlights. 224 Printf(Decor.Highlight()); 225 Range *InRange = upperBound(Min, Ranges, NumRanges); 226 for (uptr P = Min; P != Max; ++P) { 227 char Pad = ' ', Byte = ' '; 228 if (InRange && InRange->getEnd().getMemoryLocation() == P) 229 InRange = upperBound(P, Ranges, NumRanges); 230 if (!InRange && P > Loc) 231 break; 232 if (InRange && InRange->getStart().getMemoryLocation() < P) 233 Pad = '~'; 234 if (InRange && InRange->getStart().getMemoryLocation() <= P) 235 Byte = '~'; 236 char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 }; 237 Printf((P % 8 == 0) ? Buffer : &Buffer[1]); 238 } 239 Printf("%s\n", Decor.EndHighlight()); 240 241 // Go over the line again, and print names for the ranges. 242 InRange = 0; 243 unsigned Spaces = 0; 244 for (uptr P = Min; P != Max; ++P) { 245 if (!InRange || InRange->getEnd().getMemoryLocation() == P) 246 InRange = upperBound(P, Ranges, NumRanges); 247 if (!InRange) 248 break; 249 250 Spaces += (P % 8) == 0 ? 2 : 1; 251 252 if (InRange && InRange->getStart().getMemoryLocation() == P) { 253 while (Spaces--) 254 Printf(" "); 255 renderText(InRange->getText(), Args); 256 Printf("\n"); 257 // FIXME: We only support naming one range for now! 258 break; 259 } 260 261 Spaces += 2; 262 } 263 264 // FIXME: Print names for anything we can identify within the line: 265 // 266 // * If we can identify the memory itself as belonging to a particular 267 // global, stack variable, or dynamic allocation, then do so. 268 // 269 // * If we have a pointer-size, pointer-aligned range highlighted, 270 // determine whether the value of that range is a pointer to an 271 // entity which we can name, and if so, print that name. 272 // 273 // This needs an external symbolizer, or (preferably) ASan instrumentation. 274} 275 276Diag::~Diag() { 277 InitializeSanitizerCommon(); 278 Decorator Decor; 279 SpinMutexLock l(&CommonSanitizerReportMutex); 280 Printf(Decor.Bold()); 281 282 renderLocation(Loc); 283 284 switch (Level) { 285 case DL_Error: 286 Printf("%s runtime error: %s%s", 287 Decor.Warning(), Decor.EndWarning(), Decor.Bold()); 288 break; 289 290 case DL_Note: 291 Printf("%s note: %s", Decor.Note(), Decor.EndNote()); 292 break; 293 } 294 295 renderText(Message, Args); 296 297 Printf("%s\n", Decor.Default()); 298 299 if (Loc.isMemoryLocation()) 300 renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, 301 NumRanges, Args); 302} 303