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 RenderHex(InternalScopedString *Buffer, UIntMax Val) {
128#if HAVE_INT128_T
129  Buffer->append("0x%08x%08x%08x%08x", (unsigned int)(Val >> 96),
130                 (unsigned int)(Val >> 64), (unsigned int)(Val >> 32),
131                 (unsigned int)(Val));
132#else
133  UNREACHABLE("long long smaller than 64 bits?");
134#endif
135}
136
137static void RenderLocation(InternalScopedString *Buffer, Location Loc) {
138  switch (Loc.getKind()) {
139  case Location::LK_Source: {
140    SourceLocation SLoc = Loc.getSourceLocation();
141    if (SLoc.isInvalid())
142      Buffer->append("<unknown>");
143    else
144      RenderSourceLocation(Buffer, SLoc.getFilename(), SLoc.getLine(),
145                           SLoc.getColumn(), common_flags()->symbolize_vs_style,
146                           common_flags()->strip_path_prefix);
147    return;
148  }
149  case Location::LK_Memory:
150    Buffer->append("%p", Loc.getMemoryLocation());
151    return;
152  case Location::LK_Symbolized: {
153    const AddressInfo &Info = Loc.getSymbolizedStack()->info;
154    if (Info.file)
155      RenderSourceLocation(Buffer, Info.file, Info.line, Info.column,
156                           common_flags()->symbolize_vs_style,
157                           common_flags()->strip_path_prefix);
158    else if (Info.module)
159      RenderModuleLocation(Buffer, Info.module, Info.module_offset,
160                           common_flags()->strip_path_prefix);
161    else
162      Buffer->append("%p", Info.address);
163    return;
164  }
165  case Location::LK_Null:
166    Buffer->append("<unknown>");
167    return;
168  }
169}
170
171static void RenderText(InternalScopedString *Buffer, const char *Message,
172                       const Diag::Arg *Args) {
173  for (const char *Msg = Message; *Msg; ++Msg) {
174    if (*Msg != '%') {
175      Buffer->append("%c", *Msg);
176      continue;
177    }
178    const Diag::Arg &A = Args[*++Msg - '0'];
179    switch (A.Kind) {
180    case Diag::AK_String:
181      Buffer->append("%s", A.String);
182      break;
183    case Diag::AK_TypeName: {
184      if (SANITIZER_WINDOWS)
185        // The Windows implementation demangles names early.
186        Buffer->append("'%s'", A.String);
187      else
188        Buffer->append("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
189      break;
190    }
191    case Diag::AK_SInt:
192      // 'long long' is guaranteed to be at least 64 bits wide.
193      if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
194        Buffer->append("%lld", (long long)A.SInt);
195      else
196        RenderHex(Buffer, A.SInt);
197      break;
198    case Diag::AK_UInt:
199      if (A.UInt <= UINT64_MAX)
200        Buffer->append("%llu", (unsigned long long)A.UInt);
201      else
202        RenderHex(Buffer, A.UInt);
203      break;
204    case Diag::AK_Float: {
205      // FIXME: Support floating-point formatting in sanitizer_common's
206      //        printf, and stop using snprintf here.
207      char FloatBuffer[32];
208#if SANITIZER_WINDOWS
209      sprintf_s(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
210#else
211      snprintf(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
212#endif
213      Buffer->append("%s", FloatBuffer);
214      break;
215    }
216    case Diag::AK_Pointer:
217      Buffer->append("%p", A.Pointer);
218      break;
219    }
220  }
221}
222
223/// Find the earliest-starting range in Ranges which ends after Loc.
224static Range *upperBound(MemoryLocation Loc, Range *Ranges,
225                         unsigned NumRanges) {
226  Range *Best = 0;
227  for (unsigned I = 0; I != NumRanges; ++I)
228    if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
229        (!Best ||
230         Best->getStart().getMemoryLocation() >
231         Ranges[I].getStart().getMemoryLocation()))
232      Best = &Ranges[I];
233  return Best;
234}
235
236static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) {
237  return (LHS < RHS) ? 0 : LHS - RHS;
238}
239
240static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
241  const uptr Limit = (uptr)-1;
242  return (LHS > Limit - RHS) ? Limit : LHS + RHS;
243}
244
245/// Render a snippet of the address space near a location.
246static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
247                               Range *Ranges, unsigned NumRanges,
248                               const Diag::Arg *Args) {
249  // Show at least the 8 bytes surrounding Loc.
250  const unsigned MinBytesNearLoc = 4;
251  MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
252  MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc);
253  MemoryLocation OrigMin = Min;
254  for (unsigned I = 0; I < NumRanges; ++I) {
255    Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
256    Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
257  }
258
259  // If we have too many interesting bytes, prefer to show bytes after Loc.
260  const unsigned BytesToShow = 32;
261  if (Max - Min > BytesToShow)
262    Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
263  Max = addNoOverflow(Min, BytesToShow);
264
265  if (!IsAccessibleMemoryRange(Min, Max - Min)) {
266    Printf("<memory cannot be printed>\n");
267    return;
268  }
269
270  // Emit data.
271  InternalScopedString Buffer(1024);
272  for (uptr P = Min; P != Max; ++P) {
273    unsigned char C = *reinterpret_cast<const unsigned char*>(P);
274    Buffer.append("%s%02x", (P % 8 == 0) ? "  " : " ", C);
275  }
276  Buffer.append("\n");
277
278  // Emit highlights.
279  Buffer.append(Decor.Highlight());
280  Range *InRange = upperBound(Min, Ranges, NumRanges);
281  for (uptr P = Min; P != Max; ++P) {
282    char Pad = ' ', Byte = ' ';
283    if (InRange && InRange->getEnd().getMemoryLocation() == P)
284      InRange = upperBound(P, Ranges, NumRanges);
285    if (!InRange && P > Loc)
286      break;
287    if (InRange && InRange->getStart().getMemoryLocation() < P)
288      Pad = '~';
289    if (InRange && InRange->getStart().getMemoryLocation() <= P)
290      Byte = '~';
291    if (P % 8 == 0)
292      Buffer.append("%c", Pad);
293    Buffer.append("%c", Pad);
294    Buffer.append("%c", P == Loc ? '^' : Byte);
295    Buffer.append("%c", Byte);
296  }
297  Buffer.append("%s\n", Decor.EndHighlight());
298
299  // Go over the line again, and print names for the ranges.
300  InRange = 0;
301  unsigned Spaces = 0;
302  for (uptr P = Min; P != Max; ++P) {
303    if (!InRange || InRange->getEnd().getMemoryLocation() == P)
304      InRange = upperBound(P, Ranges, NumRanges);
305    if (!InRange)
306      break;
307
308    Spaces += (P % 8) == 0 ? 2 : 1;
309
310    if (InRange && InRange->getStart().getMemoryLocation() == P) {
311      while (Spaces--)
312        Buffer.append(" ");
313      RenderText(&Buffer, InRange->getText(), Args);
314      Buffer.append("\n");
315      // FIXME: We only support naming one range for now!
316      break;
317    }
318
319    Spaces += 2;
320  }
321
322  Printf("%s", Buffer.data());
323  // FIXME: Print names for anything we can identify within the line:
324  //
325  //  * If we can identify the memory itself as belonging to a particular
326  //    global, stack variable, or dynamic allocation, then do so.
327  //
328  //  * If we have a pointer-size, pointer-aligned range highlighted,
329  //    determine whether the value of that range is a pointer to an
330  //    entity which we can name, and if so, print that name.
331  //
332  // This needs an external symbolizer, or (preferably) ASan instrumentation.
333}
334
335Diag::~Diag() {
336  // All diagnostics should be printed under report mutex.
337  CommonSanitizerReportMutex.CheckLocked();
338  Decorator Decor;
339  InternalScopedString Buffer(1024);
340
341  Buffer.append(Decor.Bold());
342  RenderLocation(&Buffer, Loc);
343  Buffer.append(":");
344
345  switch (Level) {
346  case DL_Error:
347    Buffer.append("%s runtime error: %s%s", Decor.Warning(), Decor.EndWarning(),
348                  Decor.Bold());
349    break;
350
351  case DL_Note:
352    Buffer.append("%s note: %s", Decor.Note(), Decor.EndNote());
353    break;
354  }
355
356  RenderText(&Buffer, Message, Args);
357
358  Buffer.append("%s\n", Decor.Default());
359  Printf("%s", Buffer.data());
360
361  if (Loc.isMemoryLocation())
362    PrintMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, NumRanges, Args);
363}
364
365ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc,
366                           ErrorType Type)
367    : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) {
368  InitAsStandaloneIfNecessary();
369  CommonSanitizerReportMutex.Lock();
370}
371
372ScopedReport::~ScopedReport() {
373  MaybePrintStackTrace(Opts.pc, Opts.bp);
374  MaybeReportErrorSummary(SummaryLoc, Type);
375  CommonSanitizerReportMutex.Unlock();
376  if (flags()->halt_on_error)
377    Die();
378}
379
380ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
381static SuppressionContext *suppression_ctx = nullptr;
382static const char kVptrCheck[] = "vptr_check";
383static const char *kSuppressionTypes[] = {
384#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
385#include "ubsan_checks.inc"
386#undef UBSAN_CHECK
387    kVptrCheck,
388};
389
390void __ubsan::InitializeSuppressions() {
391  CHECK_EQ(nullptr, suppression_ctx);
392  suppression_ctx = new (suppression_placeholder) // NOLINT
393      SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
394  suppression_ctx->ParseFromFile(flags()->suppressions);
395}
396
397bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
398  InitAsStandaloneIfNecessary();
399  CHECK(suppression_ctx);
400  Suppression *s;
401  return suppression_ctx->Match(TypeName, kVptrCheck, &s);
402}
403
404bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
405  InitAsStandaloneIfNecessary();
406  CHECK(suppression_ctx);
407  const char *SuppType = ConvertTypeToFlagName(ET);
408  // Fast path: don't symbolize PC if there is no suppressions for given UB
409  // type.
410  if (!suppression_ctx->HasSuppressionType(SuppType))
411    return false;
412  Suppression *s = nullptr;
413  // Suppress by file name known to runtime.
414  if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
415    return true;
416  // Suppress by module name.
417  if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
418    if (suppression_ctx->Match(Module, SuppType, &s))
419      return true;
420  }
421  // Suppress by function or source file name from debug info.
422  SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
423  const AddressInfo &AI = Stack.get()->info;
424  return suppression_ctx->Match(AI.function, SuppType, &s) ||
425         suppression_ctx->Match(AI.file, SuppType, &s);
426}
427
428#endif  // CAN_SANITIZE_UB
429