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_diag.h"
15#include "sanitizer_common/sanitizer_common.h"
16#include "sanitizer_common/sanitizer_flags.h"
17#include "sanitizer_common/sanitizer_libc.h"
18#include "sanitizer_common/sanitizer_report_decorator.h"
19#include "sanitizer_common/sanitizer_stacktrace.h"
20#include "sanitizer_common/sanitizer_symbolizer.h"
21#include <stdio.h>
22
23using namespace __ubsan;
24
25static void InitializeSanitizerCommon() {
26  static StaticSpinMutex init_mu;
27  SpinMutexLock l(&init_mu);
28  static bool initialized;
29  if (initialized)
30   return;
31  if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) {
32    // UBSan is run in a standalone mode. Initialize it now.
33    SanitizerToolName = "UndefinedBehaviorSanitizer";
34    CommonFlags *cf = common_flags();
35    SetCommonFlagsDefaults(cf);
36    cf->print_summary = false;
37  }
38  initialized = true;
39}
40
41namespace {
42class Decorator : public SanitizerCommonDecorator {
43 public:
44  Decorator() : SanitizerCommonDecorator() {}
45  const char *Highlight() const { return Green(); }
46  const char *EndHighlight() const { return Default(); }
47  const char *Note() const { return Black(); }
48  const char *EndNote() const { return Default(); }
49};
50}
51
52Location __ubsan::getCallerLocation(uptr CallerLoc) {
53  if (!CallerLoc)
54    return Location();
55
56  uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
57  return getFunctionLocation(Loc, 0);
58}
59
60Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) {
61  if (!Loc)
62    return Location();
63  InitializeSanitizerCommon();
64
65  AddressInfo Info;
66  if (!Symbolizer::GetOrInit()->SymbolizePC(Loc, &Info, 1) ||
67      !Info.module || !*Info.module)
68    return Location(Loc);
69
70  if (FName && Info.function)
71    *FName = Info.function;
72
73  if (!Info.file)
74    return ModuleLocation(Info.module, Info.module_offset);
75
76  return SourceLocation(Info.file, Info.line, Info.column);
77}
78
79Diag &Diag::operator<<(const TypeDescriptor &V) {
80  return AddArg(V.getTypeName());
81}
82
83Diag &Diag::operator<<(const Value &V) {
84  if (V.getType().isSignedIntegerTy())
85    AddArg(V.getSIntValue());
86  else if (V.getType().isUnsignedIntegerTy())
87    AddArg(V.getUIntValue());
88  else if (V.getType().isFloatTy())
89    AddArg(V.getFloatValue());
90  else
91    AddArg("<unknown>");
92  return *this;
93}
94
95/// Hexadecimal printing for numbers too large for Printf to handle directly.
96static void PrintHex(UIntMax Val) {
97#if HAVE_INT128_T
98  Printf("0x%08x%08x%08x%08x",
99          (unsigned int)(Val >> 96),
100          (unsigned int)(Val >> 64),
101          (unsigned int)(Val >> 32),
102          (unsigned int)(Val));
103#else
104  UNREACHABLE("long long smaller than 64 bits?");
105#endif
106}
107
108static void renderLocation(Location Loc) {
109  InternalScopedString LocBuffer(1024);
110  switch (Loc.getKind()) {
111  case Location::LK_Source: {
112    SourceLocation SLoc = Loc.getSourceLocation();
113    if (SLoc.isInvalid())
114      LocBuffer.append("<unknown>");
115    else
116      PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
117                          SLoc.getColumn());
118    break;
119  }
120  case Location::LK_Module:
121    PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(),
122                         Loc.getModuleLocation().getOffset());
123    break;
124  case Location::LK_Memory:
125    LocBuffer.append("%p", Loc.getMemoryLocation());
126    break;
127  case Location::LK_Null:
128    LocBuffer.append("<unknown>");
129    break;
130  }
131  Printf("%s:", LocBuffer.data());
132}
133
134static void renderText(const char *Message, const Diag::Arg *Args) {
135  for (const char *Msg = Message; *Msg; ++Msg) {
136    if (*Msg != '%') {
137      char Buffer[64];
138      unsigned I;
139      for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I)
140        Buffer[I] = Msg[I];
141      Buffer[I] = '\0';
142      Printf(Buffer);
143      Msg += I - 1;
144    } else {
145      const Diag::Arg &A = Args[*++Msg - '0'];
146      switch (A.Kind) {
147      case Diag::AK_String:
148        Printf("%s", A.String);
149        break;
150      case Diag::AK_Mangled: {
151        Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
152        break;
153      }
154      case Diag::AK_SInt:
155        // 'long long' is guaranteed to be at least 64 bits wide.
156        if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
157          Printf("%lld", (long long)A.SInt);
158        else
159          PrintHex(A.SInt);
160        break;
161      case Diag::AK_UInt:
162        if (A.UInt <= UINT64_MAX)
163          Printf("%llu", (unsigned long long)A.UInt);
164        else
165          PrintHex(A.UInt);
166        break;
167      case Diag::AK_Float: {
168        // FIXME: Support floating-point formatting in sanitizer_common's
169        //        printf, and stop using snprintf here.
170        char Buffer[32];
171        snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
172        Printf("%s", Buffer);
173        break;
174      }
175      case Diag::AK_Pointer:
176        Printf("%p", A.Pointer);
177        break;
178      }
179    }
180  }
181}
182
183/// Find the earliest-starting range in Ranges which ends after Loc.
184static Range *upperBound(MemoryLocation Loc, Range *Ranges,
185                         unsigned NumRanges) {
186  Range *Best = 0;
187  for (unsigned I = 0; I != NumRanges; ++I)
188    if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
189        (!Best ||
190         Best->getStart().getMemoryLocation() >
191         Ranges[I].getStart().getMemoryLocation()))
192      Best = &Ranges[I];
193  return Best;
194}
195
196/// Render a snippet of the address space near a location.
197static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
198                                Range *Ranges, unsigned NumRanges,
199                                const Diag::Arg *Args) {
200  const unsigned BytesToShow = 32;
201  const unsigned MinBytesNearLoc = 4;
202
203  // Show at least the 8 bytes surrounding Loc.
204  MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc;
205  for (unsigned I = 0; I < NumRanges; ++I) {
206    Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
207    Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
208  }
209
210  // If we have too many interesting bytes, prefer to show bytes after Loc.
211  if (Max - Min > BytesToShow)
212    Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc);
213  Max = Min + BytesToShow;
214
215  // Emit data.
216  for (uptr P = Min; P != Max; ++P) {
217    // FIXME: Check that the address is readable before printing it.
218    unsigned char C = *reinterpret_cast<const unsigned char*>(P);
219    Printf("%s%02x", (P % 8 == 0) ? "  " : " ", C);
220  }
221  Printf("\n");
222
223  // Emit highlights.
224  Printf(Decor.Highlight());
225  Range *InRange = upperBound(Min, Ranges, NumRanges);
226  for (uptr P = Min; P != Max; ++P) {
227    char Pad = ' ', Byte = ' ';
228    if (InRange && InRange->getEnd().getMemoryLocation() == P)
229      InRange = upperBound(P, Ranges, NumRanges);
230    if (!InRange && P > Loc)
231      break;
232    if (InRange && InRange->getStart().getMemoryLocation() < P)
233      Pad = '~';
234    if (InRange && InRange->getStart().getMemoryLocation() <= P)
235      Byte = '~';
236    char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
237    Printf((P % 8 == 0) ? Buffer : &Buffer[1]);
238  }
239  Printf("%s\n", Decor.EndHighlight());
240
241  // Go over the line again, and print names for the ranges.
242  InRange = 0;
243  unsigned Spaces = 0;
244  for (uptr P = Min; P != Max; ++P) {
245    if (!InRange || InRange->getEnd().getMemoryLocation() == P)
246      InRange = upperBound(P, Ranges, NumRanges);
247    if (!InRange)
248      break;
249
250    Spaces += (P % 8) == 0 ? 2 : 1;
251
252    if (InRange && InRange->getStart().getMemoryLocation() == P) {
253      while (Spaces--)
254        Printf(" ");
255      renderText(InRange->getText(), Args);
256      Printf("\n");
257      // FIXME: We only support naming one range for now!
258      break;
259    }
260
261    Spaces += 2;
262  }
263
264  // FIXME: Print names for anything we can identify within the line:
265  //
266  //  * If we can identify the memory itself as belonging to a particular
267  //    global, stack variable, or dynamic allocation, then do so.
268  //
269  //  * If we have a pointer-size, pointer-aligned range highlighted,
270  //    determine whether the value of that range is a pointer to an
271  //    entity which we can name, and if so, print that name.
272  //
273  // This needs an external symbolizer, or (preferably) ASan instrumentation.
274}
275
276Diag::~Diag() {
277  InitializeSanitizerCommon();
278  Decorator Decor;
279  SpinMutexLock l(&CommonSanitizerReportMutex);
280  Printf(Decor.Bold());
281
282  renderLocation(Loc);
283
284  switch (Level) {
285  case DL_Error:
286    Printf("%s runtime error: %s%s",
287           Decor.Warning(), Decor.EndWarning(), Decor.Bold());
288    break;
289
290  case DL_Note:
291    Printf("%s note: %s", Decor.Note(), Decor.EndNote());
292    break;
293  }
294
295  renderText(Message, Args);
296
297  Printf("%s\n", Decor.Default());
298
299  if (Loc.isMemoryLocation())
300    renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
301                        NumRanges, Args);
302}
303