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