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