brintos

brintos / llvm-project-archived public Read only

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