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