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