1 //===-- ubsan_diag.cc -----------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // Diagnostic reporting for the UBSan runtime.
10 //===----------------------------------------------------------------------===//
12 #include "ubsan_platform.h"
14 #include "ubsan_diag.h"
15 #include "ubsan_init.h"
16 #include "ubsan_flags.h"
17 #include "sanitizer_common/sanitizer_placement_new.h"
18 #include "sanitizer_common/sanitizer_report_decorator.h"
19 #include "sanitizer_common/sanitizer_stacktrace.h"
20 #include "sanitizer_common/sanitizer_stacktrace_printer.h"
21 #include "sanitizer_common/sanitizer_suppressions.h"
22 #include "sanitizer_common/sanitizer_symbolizer.h"
25 using namespace __ubsan
;
27 static void MaybePrintStackTrace(uptr pc
, uptr bp
) {
28 // We assume that flags are already parsed, as UBSan runtime
29 // will definitely be called when we print the first diagnostics message.
30 if (!flags()->print_stacktrace
)
32 // We can only use slow unwind, as we don't have any information about stack
34 // FIXME: It's better to respect "fast_unwind_on_fatal" runtime flag and
35 // fetch stack top/bottom information if we have it (e.g. if we're running
37 if (StackTrace::WillUseFastUnwind(false))
39 BufferedStackTrace stack
;
40 stack
.Unwind(kStackTraceMax
, pc
, bp
, 0, 0, 0, false);
44 static const char *ConvertTypeToString(ErrorType Type
) {
46 #define UBSAN_CHECK(Name, SummaryKind, FlagName) \
47 case ErrorType::Name: \
49 #include "ubsan_checks.inc"
52 UNREACHABLE("unknown ErrorType!");
55 static void MaybeReportErrorSummary(Location Loc
, ErrorType Type
) {
56 if (!common_flags()->print_summary
)
58 if (!flags()->report_error_type
)
59 Type
= ErrorType::GenericUB
;
60 const char *ErrorKind
= ConvertTypeToString(Type
);
61 if (Loc
.isSourceLocation()) {
62 SourceLocation SLoc
= Loc
.getSourceLocation();
63 if (!SLoc
.isInvalid()) {
65 AI
.file
= internal_strdup(SLoc
.getFilename());
66 AI
.line
= SLoc
.getLine();
67 AI
.column
= SLoc
.getColumn();
68 AI
.function
= internal_strdup(""); // Avoid printing ?? as function name.
69 ReportErrorSummary(ErrorKind
, AI
);
73 } else if (Loc
.isSymbolizedStack()) {
74 const AddressInfo
&AI
= Loc
.getSymbolizedStack()->info
;
75 ReportErrorSummary(ErrorKind
, AI
);
78 ReportErrorSummary(ErrorKind
);
82 class Decorator
: public SanitizerCommonDecorator
{
84 Decorator() : SanitizerCommonDecorator() {}
85 const char *Highlight() const { return Green(); }
86 const char *EndHighlight() const { return Default(); }
87 const char *Note() const { return Black(); }
88 const char *EndNote() const { return Default(); }
92 SymbolizedStack
*__ubsan::getSymbolizedLocation(uptr PC
) {
93 InitAsStandaloneIfNecessary();
94 return Symbolizer::GetOrInit()->SymbolizePC(PC
);
97 Diag
&Diag::operator<<(const TypeDescriptor
&V
) {
98 return AddArg(V
.getTypeName());
101 Diag
&Diag::operator<<(const Value
&V
) {
102 if (V
.getType().isSignedIntegerTy())
103 AddArg(V
.getSIntValue());
104 else if (V
.getType().isUnsignedIntegerTy())
105 AddArg(V
.getUIntValue());
106 else if (V
.getType().isFloatTy())
107 AddArg(V
.getFloatValue());
113 /// Hexadecimal printing for numbers too large for Printf to handle directly.
114 static void PrintHex(UIntMax Val
) {
116 Printf("0x%08x%08x%08x%08x",
117 (unsigned int)(Val
>> 96),
118 (unsigned int)(Val
>> 64),
119 (unsigned int)(Val
>> 32),
120 (unsigned int)(Val
));
122 UNREACHABLE("long long smaller than 64 bits?");
126 static void renderLocation(Location Loc
) {
127 InternalScopedString
LocBuffer(1024);
128 switch (Loc
.getKind()) {
129 case Location::LK_Source
: {
130 SourceLocation SLoc
= Loc
.getSourceLocation();
131 if (SLoc
.isInvalid())
132 LocBuffer
.append("<unknown>");
134 RenderSourceLocation(&LocBuffer
, SLoc
.getFilename(), SLoc
.getLine(),
135 SLoc
.getColumn(), common_flags()->symbolize_vs_style
,
136 common_flags()->strip_path_prefix
);
139 case Location::LK_Memory
:
140 LocBuffer
.append("%p", Loc
.getMemoryLocation());
142 case Location::LK_Symbolized
: {
143 const AddressInfo
&Info
= Loc
.getSymbolizedStack()->info
;
145 RenderSourceLocation(&LocBuffer
, Info
.file
, Info
.line
, Info
.column
,
146 common_flags()->symbolize_vs_style
,
147 common_flags()->strip_path_prefix
);
148 } else if (Info
.module
) {
149 RenderModuleLocation(&LocBuffer
, Info
.module
, Info
.module_offset
,
150 common_flags()->strip_path_prefix
);
152 LocBuffer
.append("%p", Info
.address
);
156 case Location::LK_Null
:
157 LocBuffer
.append("<unknown>");
160 Printf("%s:", LocBuffer
.data());
163 static void renderText(const char *Message
, const Diag::Arg
*Args
) {
164 for (const char *Msg
= Message
; *Msg
; ++Msg
) {
168 for (I
= 0; Msg
[I
] && Msg
[I
] != '%' && I
!= 63; ++I
)
174 const Diag::Arg
&A
= Args
[*++Msg
- '0'];
176 case Diag::AK_String
:
177 Printf("%s", A
.String
);
179 case Diag::AK_TypeName
: {
180 if (SANITIZER_WINDOWS
)
181 // The Windows implementation demangles names early.
182 Printf("'%s'", A
.String
);
184 Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A
.String
));
188 // 'long long' is guaranteed to be at least 64 bits wide.
189 if (A
.SInt
>= INT64_MIN
&& A
.SInt
<= INT64_MAX
)
190 Printf("%lld", (long long)A
.SInt
);
195 if (A
.UInt
<= UINT64_MAX
)
196 Printf("%llu", (unsigned long long)A
.UInt
);
200 case Diag::AK_Float
: {
201 // FIXME: Support floating-point formatting in sanitizer_common's
202 // printf, and stop using snprintf here.
204 #if SANITIZER_WINDOWS
205 sprintf_s(Buffer
, sizeof(Buffer
), "%Lg", (long double)A
.Float
);
207 snprintf(Buffer
, sizeof(Buffer
), "%Lg", (long double)A
.Float
);
209 Printf("%s", Buffer
);
212 case Diag::AK_Pointer
:
213 Printf("%p", A
.Pointer
);
220 /// Find the earliest-starting range in Ranges which ends after Loc.
221 static Range
*upperBound(MemoryLocation Loc
, Range
*Ranges
,
222 unsigned NumRanges
) {
224 for (unsigned I
= 0; I
!= NumRanges
; ++I
)
225 if (Ranges
[I
].getEnd().getMemoryLocation() > Loc
&&
227 Best
->getStart().getMemoryLocation() >
228 Ranges
[I
].getStart().getMemoryLocation()))
233 static inline uptr
subtractNoOverflow(uptr LHS
, uptr RHS
) {
234 return (LHS
< RHS
) ? 0 : LHS
- RHS
;
237 static inline uptr
addNoOverflow(uptr LHS
, uptr RHS
) {
238 const uptr Limit
= (uptr
)-1;
239 return (LHS
> Limit
- RHS
) ? Limit
: LHS
+ RHS
;
242 /// Render a snippet of the address space near a location.
243 static void renderMemorySnippet(const Decorator
&Decor
, MemoryLocation Loc
,
244 Range
*Ranges
, unsigned NumRanges
,
245 const Diag::Arg
*Args
) {
246 // Show at least the 8 bytes surrounding Loc.
247 const unsigned MinBytesNearLoc
= 4;
248 MemoryLocation Min
= subtractNoOverflow(Loc
, MinBytesNearLoc
);
249 MemoryLocation Max
= addNoOverflow(Loc
, MinBytesNearLoc
);
250 MemoryLocation OrigMin
= Min
;
251 for (unsigned I
= 0; I
< NumRanges
; ++I
) {
252 Min
= __sanitizer::Min(Ranges
[I
].getStart().getMemoryLocation(), Min
);
253 Max
= __sanitizer::Max(Ranges
[I
].getEnd().getMemoryLocation(), Max
);
256 // If we have too many interesting bytes, prefer to show bytes after Loc.
257 const unsigned BytesToShow
= 32;
258 if (Max
- Min
> BytesToShow
)
259 Min
= __sanitizer::Min(Max
- BytesToShow
, OrigMin
);
260 Max
= addNoOverflow(Min
, BytesToShow
);
262 if (!IsAccessibleMemoryRange(Min
, Max
- Min
)) {
263 Printf("<memory cannot be printed>\n");
268 for (uptr P
= Min
; P
!= Max
; ++P
) {
269 unsigned char C
= *reinterpret_cast<const unsigned char*>(P
);
270 Printf("%s%02x", (P
% 8 == 0) ? " " : " ", C
);
275 Printf(Decor
.Highlight());
276 Range
*InRange
= upperBound(Min
, Ranges
, NumRanges
);
277 for (uptr P
= Min
; P
!= Max
; ++P
) {
278 char Pad
= ' ', Byte
= ' ';
279 if (InRange
&& InRange
->getEnd().getMemoryLocation() == P
)
280 InRange
= upperBound(P
, Ranges
, NumRanges
);
281 if (!InRange
&& P
> Loc
)
283 if (InRange
&& InRange
->getStart().getMemoryLocation() < P
)
285 if (InRange
&& InRange
->getStart().getMemoryLocation() <= P
)
287 char Buffer
[] = { Pad
, Pad
, P
== Loc
? '^' : Byte
, Byte
, 0 };
288 Printf((P
% 8 == 0) ? Buffer
: &Buffer
[1]);
290 Printf("%s\n", Decor
.EndHighlight());
292 // Go over the line again, and print names for the ranges.
295 for (uptr P
= Min
; P
!= Max
; ++P
) {
296 if (!InRange
|| InRange
->getEnd().getMemoryLocation() == P
)
297 InRange
= upperBound(P
, Ranges
, NumRanges
);
301 Spaces
+= (P
% 8) == 0 ? 2 : 1;
303 if (InRange
&& InRange
->getStart().getMemoryLocation() == P
) {
306 renderText(InRange
->getText(), Args
);
308 // FIXME: We only support naming one range for now!
315 // FIXME: Print names for anything we can identify within the line:
317 // * If we can identify the memory itself as belonging to a particular
318 // global, stack variable, or dynamic allocation, then do so.
320 // * If we have a pointer-size, pointer-aligned range highlighted,
321 // determine whether the value of that range is a pointer to an
322 // entity which we can name, and if so, print that name.
324 // This needs an external symbolizer, or (preferably) ASan instrumentation.
328 // All diagnostics should be printed under report mutex.
329 CommonSanitizerReportMutex
.CheckLocked();
331 Printf(Decor
.Bold());
337 Printf("%s runtime error: %s%s",
338 Decor
.Warning(), Decor
.EndWarning(), Decor
.Bold());
342 Printf("%s note: %s", Decor
.Note(), Decor
.EndNote());
346 renderText(Message
, Args
);
348 Printf("%s\n", Decor
.Default());
350 if (Loc
.isMemoryLocation())
351 renderMemorySnippet(Decor
, Loc
.getMemoryLocation(), Ranges
,
355 ScopedReport::ScopedReport(ReportOptions Opts
, Location SummaryLoc
,
357 : Opts(Opts
), SummaryLoc(SummaryLoc
), Type(Type
) {
358 InitAsStandaloneIfNecessary();
359 CommonSanitizerReportMutex
.Lock();
362 ScopedReport::~ScopedReport() {
363 MaybePrintStackTrace(Opts
.pc
, Opts
.bp
);
364 MaybeReportErrorSummary(SummaryLoc
, Type
);
365 CommonSanitizerReportMutex
.Unlock();
366 if (Opts
.DieAfterReport
|| flags()->halt_on_error
)
370 ALIGNED(64) static char suppression_placeholder
[sizeof(SuppressionContext
)];
371 static SuppressionContext
*suppression_ctx
= nullptr;
372 static const char kVptrCheck
[] = "vptr_check";
373 static const char *kSuppressionTypes
[] = { kVptrCheck
};
375 void __ubsan::InitializeSuppressions() {
376 CHECK_EQ(nullptr, suppression_ctx
);
377 suppression_ctx
= new (suppression_placeholder
) // NOLINT
378 SuppressionContext(kSuppressionTypes
, ARRAY_SIZE(kSuppressionTypes
));
379 suppression_ctx
->ParseFromFile(flags()->suppressions
);
382 bool __ubsan::IsVptrCheckSuppressed(const char *TypeName
) {
383 InitAsStandaloneIfNecessary();
384 CHECK(suppression_ctx
);
386 return suppression_ctx
->Match(TypeName
, kVptrCheck
, &s
);
389 #endif // CAN_SANITIZE_UB