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