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
27using 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.
35void 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
46static 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
58static 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
69static 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
80static 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
106namespace {
107class 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
115SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) {
116  InitAsStandaloneIfNecessary();
117  return Symbolizer::GetOrInit()->SymbolizePC(PC);
118}
119
120Diag &Diag::operator<<(const TypeDescriptor &V) {
121  return AddArg(V.getTypeName());
122}
123
124Diag &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.
137static 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
147static 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
181static 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.
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 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
345Diag::~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
382ScopedReport::Initializer::Initializer() { InitAsStandaloneIfNecessary(); }
383
384ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc,
385                           ErrorType Type)
386    : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) {}
387
388ScopedReport::~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
399ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
400static SuppressionContext *suppression_ctx = nullptr;
401static const char kVptrCheck[] = "vptr_check";
402static const char *kSuppressionTypes[] = {
403#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
404#include "ubsan_checks.inc"
405#undef UBSAN_CHECK
406    kVptrCheck,
407};
408
409void __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
416bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
417  InitAsStandaloneIfNecessary();
418  CHECK(suppression_ctx);
419  Suppression *s;
420  return suppression_ctx->Match(TypeName, kVptrCheck, &s);
421}
422
423bool __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