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