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