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