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