]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/ubsan/ubsan_diag.cc
Update compiler-rt to trunk r224034. This brings a number of new
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / ubsan / ubsan_diag.cc
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 "ubsan_init.h"
16 #include "ubsan_flags.h"
17 #include "sanitizer_common/sanitizer_report_decorator.h"
18 #include "sanitizer_common/sanitizer_stacktrace.h"
19 #include "sanitizer_common/sanitizer_stacktrace_printer.h"
20 #include "sanitizer_common/sanitizer_symbolizer.h"
21 #include <stdio.h>
22
23 using namespace __ubsan;
24
25 static void MaybePrintStackTrace(uptr pc, uptr bp) {
26   // We assume that flags are already parsed: InitIfNecessary
27   // will definitely be called when we print the first diagnostics message.
28   if (!flags()->print_stacktrace)
29     return;
30   // We can only use slow unwind, as we don't have any information about stack
31   // top/bottom.
32   // FIXME: It's better to respect "fast_unwind_on_fatal" runtime flag and
33   // fetch stack top/bottom information if we have it (e.g. if we're running
34   // under ASan).
35   if (StackTrace::WillUseFastUnwind(false))
36     return;
37   BufferedStackTrace stack;
38   stack.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false);
39   stack.Print();
40 }
41
42 static void MaybeReportErrorSummary(Location Loc) {
43   if (!common_flags()->print_summary)
44     return;
45   // Don't try to unwind the stack trace in UBSan summaries: just use the
46   // provided location.
47   if (Loc.isSourceLocation()) {
48     SourceLocation SLoc = Loc.getSourceLocation();
49     if (!SLoc.isInvalid()) {
50       ReportErrorSummary("undefined-behavior", SLoc.getFilename(),
51                          SLoc.getLine(), "");
52       return;
53     }
54   }
55   ReportErrorSummary("undefined-behavior");
56 }
57
58 namespace {
59 class Decorator : public SanitizerCommonDecorator {
60  public:
61   Decorator() : SanitizerCommonDecorator() {}
62   const char *Highlight() const { return Green(); }
63   const char *EndHighlight() const { return Default(); }
64   const char *Note() const { return Black(); }
65   const char *EndNote() const { return Default(); }
66 };
67 }
68
69 Location __ubsan::getCallerLocation(uptr CallerLoc) {
70   if (!CallerLoc)
71     return Location();
72
73   uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
74   return getFunctionLocation(Loc, 0);
75 }
76
77 Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) {
78   if (!Loc)
79     return Location();
80   InitIfNecessary();
81
82   SymbolizedStack *Frames = Symbolizer::GetOrInit()->SymbolizePC(Loc);
83   const AddressInfo &Info = Frames->info;
84
85   if (!Info.module) {
86     Frames->ClearAll();
87     return Location(Loc);
88   }
89
90   if (FName && Info.function)
91     *FName = internal_strdup(Info.function);
92
93   if (!Info.file) {
94     ModuleLocation MLoc(internal_strdup(Info.module), Info.module_offset);
95     Frames->ClearAll();
96     return MLoc;
97   }
98
99   SourceLocation SLoc(internal_strdup(Info.file), Info.line, Info.column);
100   Frames->ClearAll();
101   return SLoc;
102 }
103
104 Diag &Diag::operator<<(const TypeDescriptor &V) {
105   return AddArg(V.getTypeName());
106 }
107
108 Diag &Diag::operator<<(const Value &V) {
109   if (V.getType().isSignedIntegerTy())
110     AddArg(V.getSIntValue());
111   else if (V.getType().isUnsignedIntegerTy())
112     AddArg(V.getUIntValue());
113   else if (V.getType().isFloatTy())
114     AddArg(V.getFloatValue());
115   else
116     AddArg("<unknown>");
117   return *this;
118 }
119
120 /// Hexadecimal printing for numbers too large for Printf to handle directly.
121 static void PrintHex(UIntMax Val) {
122 #if HAVE_INT128_T
123   Printf("0x%08x%08x%08x%08x",
124           (unsigned int)(Val >> 96),
125           (unsigned int)(Val >> 64),
126           (unsigned int)(Val >> 32),
127           (unsigned int)(Val));
128 #else
129   UNREACHABLE("long long smaller than 64 bits?");
130 #endif
131 }
132
133 static void renderLocation(Location Loc) {
134   InternalScopedString LocBuffer(1024);
135   switch (Loc.getKind()) {
136   case Location::LK_Source: {
137     SourceLocation SLoc = Loc.getSourceLocation();
138     if (SLoc.isInvalid())
139       LocBuffer.append("<unknown>");
140     else
141       RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
142                            SLoc.getColumn(), common_flags()->strip_path_prefix);
143     break;
144   }
145   case Location::LK_Module: {
146     ModuleLocation MLoc = Loc.getModuleLocation();
147     RenderModuleLocation(&LocBuffer, MLoc.getModuleName(), MLoc.getOffset(),
148                          common_flags()->strip_path_prefix);
149     break;
150   }
151   case Location::LK_Memory:
152     LocBuffer.append("%p", Loc.getMemoryLocation());
153     break;
154   case Location::LK_Null:
155     LocBuffer.append("<unknown>");
156     break;
157   }
158   Printf("%s:", LocBuffer.data());
159 }
160
161 static void renderText(const char *Message, const Diag::Arg *Args) {
162   for (const char *Msg = Message; *Msg; ++Msg) {
163     if (*Msg != '%') {
164       char Buffer[64];
165       unsigned I;
166       for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I)
167         Buffer[I] = Msg[I];
168       Buffer[I] = '\0';
169       Printf(Buffer);
170       Msg += I - 1;
171     } else {
172       const Diag::Arg &A = Args[*++Msg - '0'];
173       switch (A.Kind) {
174       case Diag::AK_String:
175         Printf("%s", A.String);
176         break;
177       case Diag::AK_Mangled: {
178         Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
179         break;
180       }
181       case Diag::AK_SInt:
182         // 'long long' is guaranteed to be at least 64 bits wide.
183         if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
184           Printf("%lld", (long long)A.SInt);
185         else
186           PrintHex(A.SInt);
187         break;
188       case Diag::AK_UInt:
189         if (A.UInt <= UINT64_MAX)
190           Printf("%llu", (unsigned long long)A.UInt);
191         else
192           PrintHex(A.UInt);
193         break;
194       case Diag::AK_Float: {
195         // FIXME: Support floating-point formatting in sanitizer_common's
196         //        printf, and stop using snprintf here.
197         char Buffer[32];
198         snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
199         Printf("%s", Buffer);
200         break;
201       }
202       case Diag::AK_Pointer:
203         Printf("%p", A.Pointer);
204         break;
205       }
206     }
207   }
208 }
209
210 /// Find the earliest-starting range in Ranges which ends after Loc.
211 static Range *upperBound(MemoryLocation Loc, Range *Ranges,
212                          unsigned NumRanges) {
213   Range *Best = 0;
214   for (unsigned I = 0; I != NumRanges; ++I)
215     if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
216         (!Best ||
217          Best->getStart().getMemoryLocation() >
218          Ranges[I].getStart().getMemoryLocation()))
219       Best = &Ranges[I];
220   return Best;
221 }
222
223 static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) {
224   return (LHS < RHS) ? 0 : LHS - RHS;
225 }
226
227 static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
228   const uptr Limit = (uptr)-1;
229   return (LHS > Limit - RHS) ? Limit : LHS + RHS;
230 }
231
232 /// Render a snippet of the address space near a location.
233 static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
234                                 Range *Ranges, unsigned NumRanges,
235                                 const Diag::Arg *Args) {
236   // Show at least the 8 bytes surrounding Loc.
237   const unsigned MinBytesNearLoc = 4;
238   MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
239   MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc);
240   MemoryLocation OrigMin = Min;
241   for (unsigned I = 0; I < NumRanges; ++I) {
242     Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
243     Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
244   }
245
246   // If we have too many interesting bytes, prefer to show bytes after Loc.
247   const unsigned BytesToShow = 32;
248   if (Max - Min > BytesToShow)
249     Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
250   Max = addNoOverflow(Min, BytesToShow);
251
252   if (!IsAccessibleMemoryRange(Min, Max - Min)) {
253     Printf("<memory cannot be printed>\n");
254     return;
255   }
256
257   // Emit data.
258   for (uptr P = Min; P != Max; ++P) {
259     unsigned char C = *reinterpret_cast<const unsigned char*>(P);
260     Printf("%s%02x", (P % 8 == 0) ? "  " : " ", C);
261   }
262   Printf("\n");
263
264   // Emit highlights.
265   Printf(Decor.Highlight());
266   Range *InRange = upperBound(Min, Ranges, NumRanges);
267   for (uptr P = Min; P != Max; ++P) {
268     char Pad = ' ', Byte = ' ';
269     if (InRange && InRange->getEnd().getMemoryLocation() == P)
270       InRange = upperBound(P, Ranges, NumRanges);
271     if (!InRange && P > Loc)
272       break;
273     if (InRange && InRange->getStart().getMemoryLocation() < P)
274       Pad = '~';
275     if (InRange && InRange->getStart().getMemoryLocation() <= P)
276       Byte = '~';
277     char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
278     Printf((P % 8 == 0) ? Buffer : &Buffer[1]);
279   }
280   Printf("%s\n", Decor.EndHighlight());
281
282   // Go over the line again, and print names for the ranges.
283   InRange = 0;
284   unsigned Spaces = 0;
285   for (uptr P = Min; P != Max; ++P) {
286     if (!InRange || InRange->getEnd().getMemoryLocation() == P)
287       InRange = upperBound(P, Ranges, NumRanges);
288     if (!InRange)
289       break;
290
291     Spaces += (P % 8) == 0 ? 2 : 1;
292
293     if (InRange && InRange->getStart().getMemoryLocation() == P) {
294       while (Spaces--)
295         Printf(" ");
296       renderText(InRange->getText(), Args);
297       Printf("\n");
298       // FIXME: We only support naming one range for now!
299       break;
300     }
301
302     Spaces += 2;
303   }
304
305   // FIXME: Print names for anything we can identify within the line:
306   //
307   //  * If we can identify the memory itself as belonging to a particular
308   //    global, stack variable, or dynamic allocation, then do so.
309   //
310   //  * If we have a pointer-size, pointer-aligned range highlighted,
311   //    determine whether the value of that range is a pointer to an
312   //    entity which we can name, and if so, print that name.
313   //
314   // This needs an external symbolizer, or (preferably) ASan instrumentation.
315 }
316
317 Diag::~Diag() {
318   // All diagnostics should be printed under report mutex.
319   CommonSanitizerReportMutex.CheckLocked();
320   Decorator Decor;
321   Printf(Decor.Bold());
322
323   renderLocation(Loc);
324
325   switch (Level) {
326   case DL_Error:
327     Printf("%s runtime error: %s%s",
328            Decor.Warning(), Decor.EndWarning(), Decor.Bold());
329     break;
330
331   case DL_Note:
332     Printf("%s note: %s", Decor.Note(), Decor.EndNote());
333     break;
334   }
335
336   renderText(Message, Args);
337
338   Printf("%s\n", Decor.Default());
339
340   if (Loc.isMemoryLocation())
341     renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
342                         NumRanges, Args);
343 }
344
345 ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc)
346     : Opts(Opts), SummaryLoc(SummaryLoc) {
347   InitIfNecessary();
348   CommonSanitizerReportMutex.Lock();
349 }
350
351 ScopedReport::~ScopedReport() {
352   MaybePrintStackTrace(Opts.pc, Opts.bp);
353   MaybeReportErrorSummary(SummaryLoc);
354   CommonSanitizerReportMutex.Unlock();
355   if (Opts.DieAfterReport || flags()->halt_on_error)
356     Die();
357 }
358
359 bool __ubsan::MatchSuppression(const char *Str, SuppressionType Type) {
360   Suppression *s;
361   // If .preinit_array is not used, it is possible that the UBSan runtime is not
362   // initialized.
363   if (!SANITIZER_CAN_USE_PREINIT_ARRAY)
364     InitIfNecessary();
365   return SuppressionContext::Get()->Match(Str, Type, &s);
366 }