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