xref: /freebsd/contrib/llvm-project/compiler-rt/lib/ubsan/ubsan_diag.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- ubsan_diag.cpp ----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Diagnostic reporting for the UBSan runtime.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "ubsan_platform.h"
14 #if CAN_SANITIZE_UB
15 #include "ubsan_diag.h"
16 #include "ubsan_init.h"
17 #include "ubsan_flags.h"
18 #include "ubsan_monitor.h"
19 #include "sanitizer_common/sanitizer_placement_new.h"
20 #include "sanitizer_common/sanitizer_report_decorator.h"
21 #include "sanitizer_common/sanitizer_stacktrace.h"
22 #include "sanitizer_common/sanitizer_stacktrace_printer.h"
23 #include "sanitizer_common/sanitizer_suppressions.h"
24 #include "sanitizer_common/sanitizer_symbolizer.h"
25 #include <stdio.h>
26 
27 using namespace __ubsan;
28 
29 // UBSan is combined with runtimes that already provide this functionality
30 // (e.g., ASan) as well as runtimes that lack it (e.g., scudo). Tried to use
31 // weak linkage to resolve this issue which is not portable and breaks on
32 // Windows.
33 // TODO(yln): This is a temporary workaround. GetStackTrace functions will be
34 // removed in the future.
ubsan_GetStackTrace(BufferedStackTrace * stack,uptr max_depth,uptr pc,uptr bp,void * context,bool request_fast)35 void ubsan_GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc,
36                          uptr bp, void *context, bool request_fast) {
37   uptr top = 0;
38   uptr bottom = 0;
39   GetThreadStackTopAndBottom(false, &top, &bottom);
40   bool fast = StackTrace::WillUseFastUnwind(request_fast);
41   stack->Unwind(max_depth, pc, bp, context, top, bottom, fast);
42 }
43 
MaybePrintStackTrace(uptr pc,uptr bp)44 static void MaybePrintStackTrace(uptr pc, uptr bp) {
45   // We assume that flags are already parsed, as UBSan runtime
46   // will definitely be called when we print the first diagnostics message.
47   if (!flags()->print_stacktrace)
48     return;
49 
50   BufferedStackTrace stack;
51   ubsan_GetStackTrace(&stack, kStackTraceMax, pc, bp, nullptr,
52                 common_flags()->fast_unwind_on_fatal);
53   stack.Print();
54 }
55 
ConvertTypeToString(ErrorType Type)56 static const char *ConvertTypeToString(ErrorType Type) {
57   switch (Type) {
58 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName)                      \
59   case ErrorType::Name:                                                        \
60     return SummaryKind;
61 #include "ubsan_checks.inc"
62 #undef UBSAN_CHECK
63   }
64   UNREACHABLE("unknown ErrorType!");
65 }
66 
ConvertTypeToFlagName(ErrorType Type)67 static const char *ConvertTypeToFlagName(ErrorType Type) {
68   switch (Type) {
69 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName)                      \
70   case ErrorType::Name:                                                        \
71     return FSanitizeFlagName;
72 #include "ubsan_checks.inc"
73 #undef UBSAN_CHECK
74   }
75   UNREACHABLE("unknown ErrorType!");
76 }
77 
MaybeReportErrorSummary(Location Loc,ErrorType Type)78 static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
79   if (!common_flags()->print_summary)
80     return;
81   if (!flags()->report_error_type)
82     Type = ErrorType::GenericUB;
83   const char *ErrorKind = ConvertTypeToString(Type);
84   if (Loc.isSourceLocation()) {
85     SourceLocation SLoc = Loc.getSourceLocation();
86     if (!SLoc.isInvalid()) {
87       AddressInfo AI;
88       AI.file = internal_strdup(SLoc.getFilename());
89       AI.line = SLoc.getLine();
90       AI.column = SLoc.getColumn();
91       AI.function = nullptr;
92       ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
93       AI.Clear();
94       return;
95     }
96   } else if (Loc.isSymbolizedStack()) {
97     const AddressInfo &AI = Loc.getSymbolizedStack()->info;
98     ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
99     return;
100   }
101   ReportErrorSummary(ErrorKind, GetSanititizerToolName());
102 }
103 
104 namespace {
105 class Decorator : public SanitizerCommonDecorator {
106  public:
Decorator()107   Decorator() : SanitizerCommonDecorator() {}
Highlight() const108   const char *Highlight() const { return Green(); }
Note() const109   const char *Note() const { return Black(); }
110 };
111 }
112 
getSymbolizedLocation(uptr PC)113 SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) {
114   InitAsStandaloneIfNecessary();
115   return Symbolizer::GetOrInit()->SymbolizePC(PC);
116 }
117 
operator <<(const TypeDescriptor & V)118 Diag &Diag::operator<<(const TypeDescriptor &V) {
119   return AddArg(V.getTypeName());
120 }
121 
operator <<(const Value & V)122 Diag &Diag::operator<<(const Value &V) {
123   if (V.getType().isSignedIntegerTy())
124     AddArg(V.getSIntValue());
125   else if (V.getType().isUnsignedIntegerTy())
126     AddArg(V.getUIntValue());
127   else if (V.getType().isFloatTy())
128     AddArg(V.getFloatValue());
129   else
130     AddArg("<unknown>");
131   return *this;
132 }
133 
134 /// Hexadecimal printing for numbers too large for Printf to handle directly.
RenderHex(InternalScopedString * Buffer,UIntMax Val)135 static void RenderHex(InternalScopedString *Buffer, UIntMax Val) {
136 #if HAVE_INT128_T
137   Buffer->AppendF("0x%08x%08x%08x%08x", (unsigned int)(Val >> 96),
138                   (unsigned int)(Val >> 64), (unsigned int)(Val >> 32),
139                   (unsigned int)(Val));
140 #else
141   UNREACHABLE("long long smaller than 64 bits?");
142 #endif
143 }
144 
RenderLocation(InternalScopedString * Buffer,Location Loc)145 static void RenderLocation(InternalScopedString *Buffer, Location Loc) {
146   switch (Loc.getKind()) {
147   case Location::LK_Source: {
148     SourceLocation SLoc = Loc.getSourceLocation();
149     if (SLoc.isInvalid())
150       Buffer->AppendF("<unknown>");
151     else
152       StackTracePrinter::GetOrInit()->RenderSourceLocation(
153           Buffer, SLoc.getFilename(), SLoc.getLine(), SLoc.getColumn(),
154           common_flags()->symbolize_vs_style,
155           common_flags()->strip_path_prefix);
156     return;
157   }
158   case Location::LK_Memory:
159     Buffer->AppendF("%p", reinterpret_cast<void *>(Loc.getMemoryLocation()));
160     return;
161   case Location::LK_Symbolized: {
162     const AddressInfo &Info = Loc.getSymbolizedStack()->info;
163     if (Info.file)
164       StackTracePrinter::GetOrInit()->RenderSourceLocation(
165           Buffer, Info.file, Info.line, Info.column,
166           common_flags()->symbolize_vs_style,
167           common_flags()->strip_path_prefix);
168     else if (Info.module)
169       StackTracePrinter::GetOrInit()->RenderModuleLocation(
170           Buffer, Info.module, Info.module_offset, Info.module_arch,
171           common_flags()->strip_path_prefix);
172     else
173       Buffer->AppendF("%p", reinterpret_cast<void *>(Info.address));
174     return;
175   }
176   case Location::LK_Null:
177     Buffer->AppendF("<unknown>");
178     return;
179   }
180 }
181 
RenderText(InternalScopedString * Buffer,const char * Message,const Diag::Arg * Args)182 static void RenderText(InternalScopedString *Buffer, const char *Message,
183                        const Diag::Arg *Args) {
184   for (const char *Msg = Message; *Msg; ++Msg) {
185     if (*Msg != '%') {
186       Buffer->AppendF("%c", *Msg);
187       continue;
188     }
189     const Diag::Arg &A = Args[*++Msg - '0'];
190     switch (A.Kind) {
191     case Diag::AK_String:
192       Buffer->AppendF("%s", A.String);
193       break;
194     case Diag::AK_TypeName: {
195       if (SANITIZER_WINDOWS)
196         // The Windows implementation demangles names early.
197         Buffer->AppendF("'%s'", A.String);
198       else
199         Buffer->AppendF("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
200       break;
201     }
202     case Diag::AK_SInt:
203       // 'long long' is guaranteed to be at least 64 bits wide.
204       if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
205         Buffer->AppendF("%lld", (long long)A.SInt);
206       else
207         RenderHex(Buffer, A.SInt);
208       break;
209     case Diag::AK_UInt:
210       if (A.UInt <= UINT64_MAX)
211         Buffer->AppendF("%llu", (unsigned long long)A.UInt);
212       else
213         RenderHex(Buffer, A.UInt);
214       break;
215     case Diag::AK_Float: {
216       // FIXME: Support floating-point formatting in sanitizer_common's
217       //        printf, and stop using snprintf here.
218       char FloatBuffer[32];
219 #if SANITIZER_WINDOWS
220       // On MSVC platforms, long doubles are equal to regular doubles.
221       // In MinGW environments on x86, long doubles are 80 bit, but here,
222       // we're calling an MS CRT provided printf function which considers
223       // long doubles to be 64 bit. Just cast the float value to a regular
224       // double to avoid the potential ambiguity in MinGW mode.
225       sprintf_s(FloatBuffer, sizeof(FloatBuffer), "%g", (double)A.Float);
226 #else
227       snprintf(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
228 #endif
229       Buffer->Append(FloatBuffer);
230       break;
231     }
232     case Diag::AK_Pointer:
233       Buffer->AppendF("%p", A.Pointer);
234       break;
235     }
236   }
237 }
238 
239 /// Find the earliest-starting range in Ranges which ends after Loc.
upperBound(MemoryLocation Loc,Range * Ranges,unsigned NumRanges)240 static Range *upperBound(MemoryLocation Loc, Range *Ranges,
241                          unsigned NumRanges) {
242   Range *Best = 0;
243   for (unsigned I = 0; I != NumRanges; ++I)
244     if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
245         (!Best ||
246          Best->getStart().getMemoryLocation() >
247          Ranges[I].getStart().getMemoryLocation()))
248       Best = &Ranges[I];
249   return Best;
250 }
251 
subtractNoOverflow(uptr LHS,uptr RHS)252 static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) {
253   return (LHS < RHS) ? 0 : LHS - RHS;
254 }
255 
addNoOverflow(uptr LHS,uptr RHS)256 static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
257   const uptr Limit = (uptr)-1;
258   return (LHS > Limit - RHS) ? Limit : LHS + RHS;
259 }
260 
261 /// Render a snippet of the address space near a location.
PrintMemorySnippet(const Decorator & Decor,MemoryLocation Loc,Range * Ranges,unsigned NumRanges,const Diag::Arg * Args)262 static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
263                                Range *Ranges, unsigned NumRanges,
264                                const Diag::Arg *Args) {
265   // Show at least the 8 bytes surrounding Loc.
266   const unsigned MinBytesNearLoc = 4;
267   MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
268   MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc);
269   MemoryLocation OrigMin = Min;
270   for (unsigned I = 0; I < NumRanges; ++I) {
271     Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
272     Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
273   }
274 
275   // If we have too many interesting bytes, prefer to show bytes after Loc.
276   const unsigned BytesToShow = 32;
277   if (Max - Min > BytesToShow)
278     Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
279   Max = addNoOverflow(Min, BytesToShow);
280 
281   if (!IsAccessibleMemoryRange(Min, Max - Min)) {
282     Printf("<memory cannot be printed>\n");
283     return;
284   }
285 
286   // Emit data.
287   InternalScopedString Buffer;
288   for (uptr P = Min; P != Max; ++P) {
289     unsigned char C = *reinterpret_cast<const unsigned char*>(P);
290     Buffer.AppendF("%s%02x", (P % 8 == 0) ? "  " : " ", C);
291   }
292   Buffer.AppendF("\n");
293 
294   // Emit highlights.
295   Buffer.Append(Decor.Highlight());
296   Range *InRange = upperBound(Min, Ranges, NumRanges);
297   for (uptr P = Min; P != Max; ++P) {
298     char Pad = ' ', Byte = ' ';
299     if (InRange && InRange->getEnd().getMemoryLocation() == P)
300       InRange = upperBound(P, Ranges, NumRanges);
301     if (!InRange && P > Loc)
302       break;
303     if (InRange && InRange->getStart().getMemoryLocation() < P)
304       Pad = '~';
305     if (InRange && InRange->getStart().getMemoryLocation() <= P)
306       Byte = '~';
307     if (P % 8 == 0)
308       Buffer.AppendF("%c", Pad);
309     Buffer.AppendF("%c", Pad);
310     Buffer.AppendF("%c", P == Loc ? '^' : Byte);
311     Buffer.AppendF("%c", Byte);
312   }
313   Buffer.AppendF("%s\n", Decor.Default());
314 
315   // Go over the line again, and print names for the ranges.
316   InRange = 0;
317   unsigned Spaces = 0;
318   for (uptr P = Min; P != Max; ++P) {
319     if (!InRange || InRange->getEnd().getMemoryLocation() == P)
320       InRange = upperBound(P, Ranges, NumRanges);
321     if (!InRange)
322       break;
323 
324     Spaces += (P % 8) == 0 ? 2 : 1;
325 
326     if (InRange && InRange->getStart().getMemoryLocation() == P) {
327       while (Spaces--)
328         Buffer.AppendF(" ");
329       RenderText(&Buffer, InRange->getText(), Args);
330       Buffer.AppendF("\n");
331       // FIXME: We only support naming one range for now!
332       break;
333     }
334 
335     Spaces += 2;
336   }
337 
338   Printf("%s", Buffer.data());
339   // FIXME: Print names for anything we can identify within the line:
340   //
341   //  * If we can identify the memory itself as belonging to a particular
342   //    global, stack variable, or dynamic allocation, then do so.
343   //
344   //  * If we have a pointer-size, pointer-aligned range highlighted,
345   //    determine whether the value of that range is a pointer to an
346   //    entity which we can name, and if so, print that name.
347   //
348   // This needs an external symbolizer, or (preferably) ASan instrumentation.
349 }
350 
~Diag()351 Diag::~Diag() {
352   // All diagnostics should be printed under report mutex.
353   ScopedReport::CheckLocked();
354   Decorator Decor;
355   InternalScopedString Buffer;
356 
357   // Prepare a report that a monitor process can inspect.
358   if (Level == DL_Error) {
359     RenderText(&Buffer, Message, Args);
360     UndefinedBehaviorReport UBR{ConvertTypeToString(ET), Loc, Buffer};
361     Buffer.clear();
362   }
363 
364   Buffer.Append(Decor.Bold());
365   RenderLocation(&Buffer, Loc);
366   Buffer.AppendF(":");
367 
368   switch (Level) {
369   case DL_Error:
370     Buffer.AppendF("%s runtime error: %s%s", Decor.Warning(), Decor.Default(),
371                    Decor.Bold());
372     break;
373 
374   case DL_Note:
375     Buffer.AppendF("%s note: %s", Decor.Note(), Decor.Default());
376     break;
377   }
378 
379   RenderText(&Buffer, Message, Args);
380 
381   Buffer.AppendF("%s\n", Decor.Default());
382   Printf("%s", Buffer.data());
383 
384   if (Loc.isMemoryLocation())
385     PrintMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, NumRanges, Args);
386 }
387 
Initializer()388 ScopedReport::Initializer::Initializer() { InitAsStandaloneIfNecessary(); }
389 
ScopedReport(ReportOptions Opts,Location SummaryLoc,ErrorType Type)390 ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc,
391                            ErrorType Type)
392     : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) {}
393 
~ScopedReport()394 ScopedReport::~ScopedReport() {
395   MaybePrintStackTrace(Opts.pc, Opts.bp);
396   MaybeReportErrorSummary(SummaryLoc, Type);
397 
398   if (common_flags()->print_module_map >= 2)
399     DumpProcessMap();
400 
401   if (flags()->halt_on_error)
402     Die();
403 }
404 
405 alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)];
406 static SuppressionContext *suppression_ctx = nullptr;
407 static const char kVptrCheck[] = "vptr_check";
408 static const char *kSuppressionTypes[] = {
409 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
410 #include "ubsan_checks.inc"
411 #undef UBSAN_CHECK
412     kVptrCheck,
413 };
414 
InitializeSuppressions()415 void __ubsan::InitializeSuppressions() {
416   CHECK_EQ(nullptr, suppression_ctx);
417   suppression_ctx = new (suppression_placeholder)
418       SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
419   suppression_ctx->ParseFromFile(flags()->suppressions);
420 }
421 
IsVptrCheckSuppressed(const char * TypeName)422 bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
423   InitAsStandaloneIfNecessary();
424   CHECK(suppression_ctx);
425   Suppression *s;
426   return suppression_ctx->Match(TypeName, kVptrCheck, &s);
427 }
428 
IsPCSuppressed(ErrorType ET,uptr PC,const char * Filename)429 bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
430   InitAsStandaloneIfNecessary();
431   CHECK(suppression_ctx);
432   const char *SuppType = ConvertTypeToFlagName(ET);
433   // Fast path: don't symbolize PC if there is no suppressions for given UB
434   // type.
435   if (!suppression_ctx->HasSuppressionType(SuppType))
436     return false;
437   Suppression *s = nullptr;
438   // Suppress by file name known to runtime.
439   if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
440     return true;
441   // Suppress by module name.
442   if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
443     if (suppression_ctx->Match(Module, SuppType, &s))
444       return true;
445   }
446   // Suppress by function or source file name from debug info.
447   SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
448   const AddressInfo &AI = Stack.get()->info;
449   return suppression_ctx->Match(AI.function, SuppType, &s) ||
450          suppression_ctx->Match(AI.file, SuppType, &s);
451 }
452 
453 #endif  // CAN_SANITIZE_UB
454