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 void __ubsan::GetStackTraceWithPcBpAndContext(BufferedStackTrace
*stack
,
28 uptr max_depth
, uptr pc
, uptr bp
,
29 void *context
, bool fast
) {
33 GetThreadStackTopAndBottom(false, &top
, &bottom
);
34 stack
->Unwind(max_depth
, pc
, bp
, context
, top
, bottom
, fast
);
37 static void MaybePrintStackTrace(uptr pc
, uptr bp
) {
38 // We assume that flags are already parsed, as UBSan runtime
39 // will definitely be called when we print the first diagnostics message.
40 if (!flags()->print_stacktrace
)
43 BufferedStackTrace stack
;
44 GetStackTraceWithPcBpAndContext(&stack
, kStackTraceMax
, pc
, bp
, nullptr,
45 common_flags()->fast_unwind_on_fatal
);
49 static const char *ConvertTypeToString(ErrorType Type
) {
51 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
52 case ErrorType::Name: \
54 #include "ubsan_checks.inc"
57 UNREACHABLE("unknown ErrorType!");
60 static const char *ConvertTypeToFlagName(ErrorType Type
) {
62 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
63 case ErrorType::Name: \
64 return FSanitizeFlagName;
65 #include "ubsan_checks.inc"
68 UNREACHABLE("unknown ErrorType!");
71 static void MaybeReportErrorSummary(Location Loc
, ErrorType Type
) {
72 if (!common_flags()->print_summary
)
74 if (!flags()->report_error_type
)
75 Type
= ErrorType::GenericUB
;
76 const char *ErrorKind
= ConvertTypeToString(Type
);
77 if (Loc
.isSourceLocation()) {
78 SourceLocation SLoc
= Loc
.getSourceLocation();
79 if (!SLoc
.isInvalid()) {
81 AI
.file
= internal_strdup(SLoc
.getFilename());
82 AI
.line
= SLoc
.getLine();
83 AI
.column
= SLoc
.getColumn();
84 AI
.function
= internal_strdup(""); // Avoid printing ?? as function name.
85 ReportErrorSummary(ErrorKind
, AI
, GetSanititizerToolName());
89 } else if (Loc
.isSymbolizedStack()) {
90 const AddressInfo
&AI
= Loc
.getSymbolizedStack()->info
;
91 ReportErrorSummary(ErrorKind
, AI
, GetSanititizerToolName());
94 ReportErrorSummary(ErrorKind
, GetSanititizerToolName());
98 class Decorator
: public SanitizerCommonDecorator
{
100 Decorator() : SanitizerCommonDecorator() {}
101 const char *Highlight() const { return Green(); }
102 const char *Note() const { return Black(); }
106 SymbolizedStack
*__ubsan::getSymbolizedLocation(uptr PC
) {
107 InitAsStandaloneIfNecessary();
108 return Symbolizer::GetOrInit()->SymbolizePC(PC
);
111 Diag
&Diag::operator<<(const TypeDescriptor
&V
) {
112 return AddArg(V
.getTypeName());
115 Diag
&Diag::operator<<(const Value
&V
) {
116 if (V
.getType().isSignedIntegerTy())
117 AddArg(V
.getSIntValue());
118 else if (V
.getType().isUnsignedIntegerTy())
119 AddArg(V
.getUIntValue());
120 else if (V
.getType().isFloatTy())
121 AddArg(V
.getFloatValue());
127 /// Hexadecimal printing for numbers too large for Printf to handle directly.
128 static void RenderHex(InternalScopedString
*Buffer
, UIntMax Val
) {
130 Buffer
->append("0x%08x%08x%08x%08x", (unsigned int)(Val
>> 96),
131 (unsigned int)(Val
>> 64), (unsigned int)(Val
>> 32),
132 (unsigned int)(Val
));
134 UNREACHABLE("long long smaller than 64 bits?");
138 static void RenderLocation(InternalScopedString
*Buffer
, Location Loc
) {
139 switch (Loc
.getKind()) {
140 case Location::LK_Source
: {
141 SourceLocation SLoc
= Loc
.getSourceLocation();
142 if (SLoc
.isInvalid())
143 Buffer
->append("<unknown>");
145 RenderSourceLocation(Buffer
, SLoc
.getFilename(), SLoc
.getLine(),
146 SLoc
.getColumn(), common_flags()->symbolize_vs_style
,
147 common_flags()->strip_path_prefix
);
150 case Location::LK_Memory
:
151 Buffer
->append("%p", Loc
.getMemoryLocation());
153 case Location::LK_Symbolized
: {
154 const AddressInfo
&Info
= Loc
.getSymbolizedStack()->info
;
156 RenderSourceLocation(Buffer
, Info
.file
, Info
.line
, Info
.column
,
157 common_flags()->symbolize_vs_style
,
158 common_flags()->strip_path_prefix
);
159 else if (Info
.module
)
160 RenderModuleLocation(Buffer
, Info
.module
, Info
.module_offset
,
161 Info
.module_arch
, common_flags()->strip_path_prefix
);
163 Buffer
->append("%p", Info
.address
);
166 case Location::LK_Null
:
167 Buffer
->append("<unknown>");
172 static void RenderText(InternalScopedString
*Buffer
, const char *Message
,
173 const Diag::Arg
*Args
) {
174 for (const char *Msg
= Message
; *Msg
; ++Msg
) {
176 Buffer
->append("%c", *Msg
);
179 const Diag::Arg
&A
= Args
[*++Msg
- '0'];
181 case Diag::AK_String
:
182 Buffer
->append("%s", A
.String
);
184 case Diag::AK_TypeName
: {
185 if (SANITIZER_WINDOWS
)
186 // The Windows implementation demangles names early.
187 Buffer
->append("'%s'", A
.String
);
189 Buffer
->append("'%s'", Symbolizer::GetOrInit()->Demangle(A
.String
));
193 // 'long long' is guaranteed to be at least 64 bits wide.
194 if (A
.SInt
>= INT64_MIN
&& A
.SInt
<= INT64_MAX
)
195 Buffer
->append("%lld", (long long)A
.SInt
);
197 RenderHex(Buffer
, A
.SInt
);
200 if (A
.UInt
<= UINT64_MAX
)
201 Buffer
->append("%llu", (unsigned long long)A
.UInt
);
203 RenderHex(Buffer
, A
.UInt
);
205 case Diag::AK_Float
: {
206 // FIXME: Support floating-point formatting in sanitizer_common's
207 // printf, and stop using snprintf here.
208 char FloatBuffer
[32];
209 #if SANITIZER_WINDOWS
210 sprintf_s(FloatBuffer
, sizeof(FloatBuffer
), "%Lg", (long double)A
.Float
);
212 snprintf(FloatBuffer
, sizeof(FloatBuffer
), "%Lg", (long double)A
.Float
);
214 Buffer
->append("%s", FloatBuffer
);
217 case Diag::AK_Pointer
:
218 Buffer
->append("%p", A
.Pointer
);
224 /// Find the earliest-starting range in Ranges which ends after Loc.
225 static Range
*upperBound(MemoryLocation Loc
, Range
*Ranges
,
226 unsigned NumRanges
) {
228 for (unsigned I
= 0; I
!= NumRanges
; ++I
)
229 if (Ranges
[I
].getEnd().getMemoryLocation() > Loc
&&
231 Best
->getStart().getMemoryLocation() >
232 Ranges
[I
].getStart().getMemoryLocation()))
237 static inline uptr
subtractNoOverflow(uptr LHS
, uptr RHS
) {
238 return (LHS
< RHS
) ? 0 : LHS
- RHS
;
241 static inline uptr
addNoOverflow(uptr LHS
, uptr RHS
) {
242 const uptr Limit
= (uptr
)-1;
243 return (LHS
> Limit
- RHS
) ? Limit
: LHS
+ RHS
;
246 /// Render a snippet of the address space near a location.
247 static void PrintMemorySnippet(const Decorator
&Decor
, MemoryLocation Loc
,
248 Range
*Ranges
, unsigned NumRanges
,
249 const Diag::Arg
*Args
) {
250 // Show at least the 8 bytes surrounding Loc.
251 const unsigned MinBytesNearLoc
= 4;
252 MemoryLocation Min
= subtractNoOverflow(Loc
, MinBytesNearLoc
);
253 MemoryLocation Max
= addNoOverflow(Loc
, MinBytesNearLoc
);
254 MemoryLocation OrigMin
= Min
;
255 for (unsigned I
= 0; I
< NumRanges
; ++I
) {
256 Min
= __sanitizer::Min(Ranges
[I
].getStart().getMemoryLocation(), Min
);
257 Max
= __sanitizer::Max(Ranges
[I
].getEnd().getMemoryLocation(), Max
);
260 // If we have too many interesting bytes, prefer to show bytes after Loc.
261 const unsigned BytesToShow
= 32;
262 if (Max
- Min
> BytesToShow
)
263 Min
= __sanitizer::Min(Max
- BytesToShow
, OrigMin
);
264 Max
= addNoOverflow(Min
, BytesToShow
);
266 if (!IsAccessibleMemoryRange(Min
, Max
- Min
)) {
267 Printf("<memory cannot be printed>\n");
272 InternalScopedString
Buffer(1024);
273 for (uptr P
= Min
; P
!= Max
; ++P
) {
274 unsigned char C
= *reinterpret_cast<const unsigned char*>(P
);
275 Buffer
.append("%s%02x", (P
% 8 == 0) ? " " : " ", C
);
280 Buffer
.append(Decor
.Highlight());
281 Range
*InRange
= upperBound(Min
, Ranges
, NumRanges
);
282 for (uptr P
= Min
; P
!= Max
; ++P
) {
283 char Pad
= ' ', Byte
= ' ';
284 if (InRange
&& InRange
->getEnd().getMemoryLocation() == P
)
285 InRange
= upperBound(P
, Ranges
, NumRanges
);
286 if (!InRange
&& P
> Loc
)
288 if (InRange
&& InRange
->getStart().getMemoryLocation() < P
)
290 if (InRange
&& InRange
->getStart().getMemoryLocation() <= P
)
293 Buffer
.append("%c", Pad
);
294 Buffer
.append("%c", Pad
);
295 Buffer
.append("%c", P
== Loc
? '^' : Byte
);
296 Buffer
.append("%c", Byte
);
298 Buffer
.append("%s\n", Decor
.Default());
300 // Go over the line again, and print names for the ranges.
303 for (uptr P
= Min
; P
!= Max
; ++P
) {
304 if (!InRange
|| InRange
->getEnd().getMemoryLocation() == P
)
305 InRange
= upperBound(P
, Ranges
, NumRanges
);
309 Spaces
+= (P
% 8) == 0 ? 2 : 1;
311 if (InRange
&& InRange
->getStart().getMemoryLocation() == P
) {
314 RenderText(&Buffer
, InRange
->getText(), Args
);
316 // FIXME: We only support naming one range for now!
323 Printf("%s", Buffer
.data());
324 // FIXME: Print names for anything we can identify within the line:
326 // * If we can identify the memory itself as belonging to a particular
327 // global, stack variable, or dynamic allocation, then do so.
329 // * If we have a pointer-size, pointer-aligned range highlighted,
330 // determine whether the value of that range is a pointer to an
331 // entity which we can name, and if so, print that name.
333 // This needs an external symbolizer, or (preferably) ASan instrumentation.
337 // All diagnostics should be printed under report mutex.
338 ScopedReport::CheckLocked();
340 InternalScopedString
Buffer(1024);
342 Buffer
.append(Decor
.Bold());
343 RenderLocation(&Buffer
, Loc
);
348 Buffer
.append("%s runtime error: %s%s", Decor
.Warning(), Decor
.Default(),
353 Buffer
.append("%s note: %s", Decor
.Note(), Decor
.Default());
357 RenderText(&Buffer
, Message
, Args
);
359 Buffer
.append("%s\n", Decor
.Default());
360 Printf("%s", Buffer
.data());
362 if (Loc
.isMemoryLocation())
363 PrintMemorySnippet(Decor
, Loc
.getMemoryLocation(), Ranges
, NumRanges
, Args
);
366 ScopedReport::Initializer::Initializer() { InitAsStandaloneIfNecessary(); }
368 ScopedReport::ScopedReport(ReportOptions Opts
, Location SummaryLoc
,
370 : Opts(Opts
), SummaryLoc(SummaryLoc
), Type(Type
) {}
372 ScopedReport::~ScopedReport() {
373 MaybePrintStackTrace(Opts
.pc
, Opts
.bp
);
374 MaybeReportErrorSummary(SummaryLoc
, Type
);
375 if (flags()->halt_on_error
)
379 ALIGNED(64) static char suppression_placeholder
[sizeof(SuppressionContext
)];
380 static SuppressionContext
*suppression_ctx
= nullptr;
381 static const char kVptrCheck
[] = "vptr_check";
382 static const char *kSuppressionTypes
[] = {
383 #define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
384 #include "ubsan_checks.inc"
389 void __ubsan::InitializeSuppressions() {
390 CHECK_EQ(nullptr, suppression_ctx
);
391 suppression_ctx
= new (suppression_placeholder
) // NOLINT
392 SuppressionContext(kSuppressionTypes
, ARRAY_SIZE(kSuppressionTypes
));
393 suppression_ctx
->ParseFromFile(flags()->suppressions
);
396 bool __ubsan::IsVptrCheckSuppressed(const char *TypeName
) {
397 InitAsStandaloneIfNecessary();
398 CHECK(suppression_ctx
);
400 return suppression_ctx
->Match(TypeName
, kVptrCheck
, &s
);
403 bool __ubsan::IsPCSuppressed(ErrorType ET
, uptr PC
, const char *Filename
) {
404 InitAsStandaloneIfNecessary();
405 CHECK(suppression_ctx
);
406 const char *SuppType
= ConvertTypeToFlagName(ET
);
407 // Fast path: don't symbolize PC if there is no suppressions for given UB
409 if (!suppression_ctx
->HasSuppressionType(SuppType
))
411 Suppression
*s
= nullptr;
412 // Suppress by file name known to runtime.
413 if (Filename
!= nullptr && suppression_ctx
->Match(Filename
, SuppType
, &s
))
415 // Suppress by module name.
416 if (const char *Module
= Symbolizer::GetOrInit()->GetModuleNameForPc(PC
)) {
417 if (suppression_ctx
->Match(Module
, SuppType
, &s
))
420 // Suppress by function or source file name from debug info.
421 SymbolizedStackHolder
Stack(Symbolizer::GetOrInit()->SymbolizePC(PC
));
422 const AddressInfo
&AI
= Stack
.get()->info
;
423 return suppression_ctx
->Match(AI
.function
, SuppType
, &s
) ||
424 suppression_ctx
->Match(AI
.file
, SuppType
, &s
);
427 #endif // CAN_SANITIZE_UB