aboutsummaryrefslogtreecommitdiff
path: root/libsanitizer/ubsan
diff options
context:
space:
mode:
authorMaxim Ostapenko <m.ostapenko@samsung.com>2016-11-08 22:04:09 +0000
committerMaxim Ostapenko <chefmax@gcc.gnu.org>2016-11-09 00:04:09 +0200
commit1018981977de9f2056cdfcd8173458e85a3751f2 (patch)
tree9c1a8b279416b5f379d7631c1b7f36ab18797212 /libsanitizer/ubsan
parentf31d9224e6346c775648139ae3f5acf3b70582e0 (diff)
All source files: Merge from upstream 285547.
libsanitizer/ * All source files: Merge from upstream 285547. * configure.tgt (SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS): New variable. * configure.ac (SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS): Handle it. * asan/Makefile.am (asan_files): Add new files. * asan/Makefile.in: Regenerate. * ubsan/Makefile.in: Likewise. * lsan/Makefile.in: Likewise. * tsan/Makefile.am (tsan_files): Add new files. * tsan/Makefile.in: Regenerate. * sanitizer_common/Makefile.am (sanitizer_common_files): Add new files. (EXTRA_libsanitizer_common_la_SOURCES): Define. (libsanitizer_common_la_LIBADD): Likewise. (libsanitizer_common_la_DEPENDENCIES): Likewise. * sanitizer_common/Makefile.in: Regenerate. * interception/Makefile.in: Likewise. * libbacktace/Makefile.in: Likewise. * Makefile.in: Likewise. * configure: Likewise. * merge.sh: Handle builtins/assembly.h merging. * builtins/assembly.h: New file. * asan/libtool-version: Bump the libasan SONAME. From-SVN: r241977
Diffstat (limited to 'libsanitizer/ubsan')
-rw-r--r--libsanitizer/ubsan/Makefile.in1
-rw-r--r--libsanitizer/ubsan/ubsan_checks.inc56
-rw-r--r--libsanitizer/ubsan/ubsan_diag.cc229
-rw-r--r--libsanitizer/ubsan/ubsan_diag.h21
-rw-r--r--libsanitizer/ubsan/ubsan_flags.cc2
-rw-r--r--libsanitizer/ubsan/ubsan_handlers.cc254
-rw-r--r--libsanitizer/ubsan/ubsan_handlers.h19
-rw-r--r--libsanitizer/ubsan/ubsan_handlers_cxx.cc105
-rw-r--r--libsanitizer/ubsan/ubsan_handlers_cxx.h14
-rw-r--r--libsanitizer/ubsan/ubsan_init.cc1
-rw-r--r--libsanitizer/ubsan/ubsan_platform.h3
-rw-r--r--libsanitizer/ubsan/ubsan_type_hash.h4
-rw-r--r--libsanitizer/ubsan/ubsan_type_hash_itanium.cc22
-rw-r--r--libsanitizer/ubsan/ubsan_value.cc4
14 files changed, 440 insertions, 295 deletions
diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in
index 51d4da58516..a816d095f6a 100644
--- a/libsanitizer/ubsan/Makefile.in
+++ b/libsanitizer/ubsan/Makefile.in
@@ -208,6 +208,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
RPC_DEFS = @RPC_DEFS@
+SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS = @SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
diff --git a/libsanitizer/ubsan/ubsan_checks.inc b/libsanitizer/ubsan/ubsan_checks.inc
index 935d82c5079..ea85877198a 100644
--- a/libsanitizer/ubsan/ubsan_checks.inc
+++ b/libsanitizer/ubsan/ubsan_checks.inc
@@ -12,40 +12,32 @@
# error "Define UBSAN_CHECK prior to including this file!"
#endif
-// UBSAN_CHECK(Name, SummaryKind, FlagName)
-// SummaryKind and FlagName should be string literals.
+// UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName)
+// SummaryKind and FSanitizeFlagName should be string literals.
-UBSAN_CHECK(GenericUB, "undefined-behavior", "-fsanitize=undefined")
-UBSAN_CHECK(NullPointerUse, "null-pointer-use", "-fsanitize=null")
-UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use",
- "-fsanitize=alignment")
-UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size",
- "-fsanitize=object-size")
+UBSAN_CHECK(GenericUB, "undefined-behavior", "undefined")
+UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null")
+UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment")
+UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size")
UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow",
- "-fsanitize=signed-integer-overflow")
+ "signed-integer-overflow")
UBSAN_CHECK(UnsignedIntegerOverflow, "unsigned-integer-overflow",
- "-fsanitize=unsigned-integer-overflow")
+ "unsigned-integer-overflow")
UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero",
- "-fsanitize=integer-divide-by-zero")
-UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero",
- "-fsanitize=float-divide-by-zero")
-UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "-fsanitize=shift-base")
-UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent",
- "-fsanitize=shift-exponent")
-UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "-fsanitize=bounds")
-UBSAN_CHECK(UnreachableCall, "unreachable-call", "-fsanitize=unreachable")
-UBSAN_CHECK(MissingReturn, "missing-return", "-fsanitize=return")
-UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index",
- "-fsanitize=vla-bound")
-UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow",
- "-fsanitize=float-cast-overflow")
-UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "-fsanitize=bool")
-UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "-fsanitize=enum")
-UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch",
- "-fsanitize=function")
+ "integer-divide-by-zero")
+UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero")
+UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "shift-base")
+UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", "shift-exponent")
+UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds")
+UBSAN_CHECK(UnreachableCall, "unreachable-call", "unreachable")
+UBSAN_CHECK(MissingReturn, "missing-return", "return")
+UBSAN_CHECK(NonPositiveVLAIndex, "non-positive-vla-index", "vla-bound")
+UBSAN_CHECK(FloatCastOverflow, "float-cast-overflow", "float-cast-overflow")
+UBSAN_CHECK(InvalidBoolLoad, "invalid-bool-load", "bool")
+UBSAN_CHECK(InvalidEnumLoad, "invalid-enum-load", "enum")
+UBSAN_CHECK(FunctionTypeMismatch, "function-type-mismatch", "function")
UBSAN_CHECK(InvalidNullReturn, "invalid-null-return",
- "-fsanitize=returns-nonnull-attribute")
-UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument",
- "-fsanitize=nonnull-attribute")
-UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "-fsanitize=vptr")
-UBSAN_CHECK(CFIBadType, "cfi-bad-type", "-fsanitize=cfi")
+ "returns-nonnull-attribute")
+UBSAN_CHECK(InvalidNullArgument, "invalid-null-argument", "nonnull-attribute")
+UBSAN_CHECK(DynamicTypeMismatch, "dynamic-type-mismatch", "vptr")
+UBSAN_CHECK(CFIBadType, "cfi-bad-type", "cfi")
diff --git a/libsanitizer/ubsan/ubsan_diag.cc b/libsanitizer/ubsan/ubsan_diag.cc
index 1197f837f75..b6e982a170e 100644
--- a/libsanitizer/ubsan/ubsan_diag.cc
+++ b/libsanitizer/ubsan/ubsan_diag.cc
@@ -43,7 +43,7 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) {
static const char *ConvertTypeToString(ErrorType Type) {
switch (Type) {
-#define UBSAN_CHECK(Name, SummaryKind, FlagName) \
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
case ErrorType::Name: \
return SummaryKind;
#include "ubsan_checks.inc"
@@ -52,6 +52,17 @@ static const char *ConvertTypeToString(ErrorType Type) {
UNREACHABLE("unknown ErrorType!");
}
+static const char *ConvertTypeToFlagName(ErrorType Type) {
+ switch (Type) {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
+ case ErrorType::Name: \
+ return FSanitizeFlagName;
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+ }
+ UNREACHABLE("unknown ErrorType!");
+}
+
static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
if (!common_flags()->print_summary)
return;
@@ -111,108 +122,98 @@ Diag &Diag::operator<<(const Value &V) {
}
/// Hexadecimal printing for numbers too large for Printf to handle directly.
-static void PrintHex(UIntMax Val) {
+static void RenderHex(InternalScopedString *Buffer, UIntMax Val) {
#if HAVE_INT128_T
- Printf("0x%08x%08x%08x%08x",
- (unsigned int)(Val >> 96),
- (unsigned int)(Val >> 64),
- (unsigned int)(Val >> 32),
- (unsigned int)(Val));
+ Buffer->append("0x%08x%08x%08x%08x", (unsigned int)(Val >> 96),
+ (unsigned int)(Val >> 64), (unsigned int)(Val >> 32),
+ (unsigned int)(Val));
#else
UNREACHABLE("long long smaller than 64 bits?");
#endif
}
-static void renderLocation(Location Loc) {
- InternalScopedString LocBuffer(1024);
+static void RenderLocation(InternalScopedString *Buffer, Location Loc) {
switch (Loc.getKind()) {
case Location::LK_Source: {
SourceLocation SLoc = Loc.getSourceLocation();
if (SLoc.isInvalid())
- LocBuffer.append("<unknown>");
+ Buffer->append("<unknown>");
else
- RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
+ RenderSourceLocation(Buffer, SLoc.getFilename(), SLoc.getLine(),
SLoc.getColumn(), common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix);
- break;
+ return;
}
case Location::LK_Memory:
- LocBuffer.append("%p", Loc.getMemoryLocation());
- break;
+ Buffer->append("%p", Loc.getMemoryLocation());
+ return;
case Location::LK_Symbolized: {
const AddressInfo &Info = Loc.getSymbolizedStack()->info;
- if (Info.file) {
- RenderSourceLocation(&LocBuffer, Info.file, Info.line, Info.column,
+ if (Info.file)
+ RenderSourceLocation(Buffer, Info.file, Info.line, Info.column,
common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix);
- } else if (Info.module) {
- RenderModuleLocation(&LocBuffer, Info.module, Info.module_offset,
+ else if (Info.module)
+ RenderModuleLocation(Buffer, Info.module, Info.module_offset,
common_flags()->strip_path_prefix);
- } else {
- LocBuffer.append("%p", Info.address);
- }
- break;
+ else
+ Buffer->append("%p", Info.address);
+ return;
}
case Location::LK_Null:
- LocBuffer.append("<unknown>");
- break;
+ Buffer->append("<unknown>");
+ return;
}
- Printf("%s:", LocBuffer.data());
}
-static void renderText(const char *Message, const Diag::Arg *Args) {
+static void RenderText(InternalScopedString *Buffer, const char *Message,
+ const Diag::Arg *Args) {
for (const char *Msg = Message; *Msg; ++Msg) {
if (*Msg != '%') {
- char Buffer[64];
- unsigned I;
- for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I)
- Buffer[I] = Msg[I];
- Buffer[I] = '\0';
- Printf(Buffer);
- Msg += I - 1;
- } else {
- const Diag::Arg &A = Args[*++Msg - '0'];
- switch (A.Kind) {
- case Diag::AK_String:
- Printf("%s", A.String);
- break;
- case Diag::AK_TypeName: {
- if (SANITIZER_WINDOWS)
- // The Windows implementation demangles names early.
- Printf("'%s'", A.String);
- else
- Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
- break;
- }
- case Diag::AK_SInt:
- // 'long long' is guaranteed to be at least 64 bits wide.
- if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
- Printf("%lld", (long long)A.SInt);
- else
- PrintHex(A.SInt);
- break;
- case Diag::AK_UInt:
- if (A.UInt <= UINT64_MAX)
- Printf("%llu", (unsigned long long)A.UInt);
- else
- PrintHex(A.UInt);
- break;
- case Diag::AK_Float: {
- // FIXME: Support floating-point formatting in sanitizer_common's
- // printf, and stop using snprintf here.
- char Buffer[32];
+ Buffer->append("%c", *Msg);
+ continue;
+ }
+ const Diag::Arg &A = Args[*++Msg - '0'];
+ switch (A.Kind) {
+ case Diag::AK_String:
+ Buffer->append("%s", A.String);
+ break;
+ case Diag::AK_TypeName: {
+ if (SANITIZER_WINDOWS)
+ // The Windows implementation demangles names early.
+ Buffer->append("'%s'", A.String);
+ else
+ Buffer->append("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
+ break;
+ }
+ case Diag::AK_SInt:
+ // 'long long' is guaranteed to be at least 64 bits wide.
+ if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
+ Buffer->append("%lld", (long long)A.SInt);
+ else
+ RenderHex(Buffer, A.SInt);
+ break;
+ case Diag::AK_UInt:
+ if (A.UInt <= UINT64_MAX)
+ Buffer->append("%llu", (unsigned long long)A.UInt);
+ else
+ RenderHex(Buffer, A.UInt);
+ break;
+ case Diag::AK_Float: {
+ // FIXME: Support floating-point formatting in sanitizer_common's
+ // printf, and stop using snprintf here.
+ char FloatBuffer[32];
#if SANITIZER_WINDOWS
- sprintf_s(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+ sprintf_s(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
#else
- snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+ snprintf(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
#endif
- Printf("%s", Buffer);
- break;
- }
- case Diag::AK_Pointer:
- Printf("%p", A.Pointer);
- break;
- }
+ Buffer->append("%s", FloatBuffer);
+ break;
+ }
+ case Diag::AK_Pointer:
+ Buffer->append("%p", A.Pointer);
+ break;
}
}
}
@@ -240,9 +241,9 @@ static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
}
/// Render a snippet of the address space near a location.
-static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
- Range *Ranges, unsigned NumRanges,
- const Diag::Arg *Args) {
+static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
+ Range *Ranges, unsigned NumRanges,
+ const Diag::Arg *Args) {
// Show at least the 8 bytes surrounding Loc.
const unsigned MinBytesNearLoc = 4;
MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
@@ -265,14 +266,15 @@ static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
}
// Emit data.
+ InternalScopedString Buffer(1024);
for (uptr P = Min; P != Max; ++P) {
unsigned char C = *reinterpret_cast<const unsigned char*>(P);
- Printf("%s%02x", (P % 8 == 0) ? " " : " ", C);
+ Buffer.append("%s%02x", (P % 8 == 0) ? " " : " ", C);
}
- Printf("\n");
+ Buffer.append("\n");
// Emit highlights.
- Printf(Decor.Highlight());
+ Buffer.append(Decor.Highlight());
Range *InRange = upperBound(Min, Ranges, NumRanges);
for (uptr P = Min; P != Max; ++P) {
char Pad = ' ', Byte = ' ';
@@ -284,10 +286,13 @@ static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
Pad = '~';
if (InRange && InRange->getStart().getMemoryLocation() <= P)
Byte = '~';
- char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
- Printf((P % 8 == 0) ? Buffer : &Buffer[1]);
+ if (P % 8 == 0)
+ Buffer.append("%c", Pad);
+ Buffer.append("%c", Pad);
+ Buffer.append("%c", P == Loc ? '^' : Byte);
+ Buffer.append("%c", Byte);
}
- Printf("%s\n", Decor.EndHighlight());
+ Buffer.append("%s\n", Decor.EndHighlight());
// Go over the line again, and print names for the ranges.
InRange = 0;
@@ -302,9 +307,9 @@ static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
if (InRange && InRange->getStart().getMemoryLocation() == P) {
while (Spaces--)
- Printf(" ");
- renderText(InRange->getText(), Args);
- Printf("\n");
+ Buffer.append(" ");
+ RenderText(&Buffer, InRange->getText(), Args);
+ Buffer.append("\n");
// FIXME: We only support naming one range for now!
break;
}
@@ -312,6 +317,7 @@ static void renderMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
Spaces += 2;
}
+ Printf("%s", Buffer.data());
// FIXME: Print names for anything we can identify within the line:
//
// * If we can identify the memory itself as belonging to a particular
@@ -328,28 +334,30 @@ Diag::~Diag() {
// All diagnostics should be printed under report mutex.
CommonSanitizerReportMutex.CheckLocked();
Decorator Decor;
- Printf(Decor.Bold());
+ InternalScopedString Buffer(1024);
- renderLocation(Loc);
+ Buffer.append(Decor.Bold());
+ RenderLocation(&Buffer, Loc);
+ Buffer.append(":");
switch (Level) {
case DL_Error:
- Printf("%s runtime error: %s%s",
- Decor.Warning(), Decor.EndWarning(), Decor.Bold());
+ Buffer.append("%s runtime error: %s%s", Decor.Warning(), Decor.EndWarning(),
+ Decor.Bold());
break;
case DL_Note:
- Printf("%s note: %s", Decor.Note(), Decor.EndNote());
+ Buffer.append("%s note: %s", Decor.Note(), Decor.EndNote());
break;
}
- renderText(Message, Args);
+ RenderText(&Buffer, Message, Args);
- Printf("%s\n", Decor.Default());
+ Buffer.append("%s\n", Decor.Default());
+ Printf("%s", Buffer.data());
if (Loc.isMemoryLocation())
- renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges,
- NumRanges, Args);
+ PrintMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, NumRanges, Args);
}
ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc,
@@ -363,14 +371,19 @@ ScopedReport::~ScopedReport() {
MaybePrintStackTrace(Opts.pc, Opts.bp);
MaybeReportErrorSummary(SummaryLoc, Type);
CommonSanitizerReportMutex.Unlock();
- if (Opts.DieAfterReport || flags()->halt_on_error)
+ if (flags()->halt_on_error)
Die();
}
ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx = nullptr;
static const char kVptrCheck[] = "vptr_check";
-static const char *kSuppressionTypes[] = { kVptrCheck };
+static const char *kSuppressionTypes[] = {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+ kVptrCheck,
+};
void __ubsan::InitializeSuppressions() {
CHECK_EQ(nullptr, suppression_ctx);
@@ -386,4 +399,28 @@ bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
return suppression_ctx->Match(TypeName, kVptrCheck, &s);
}
+bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
+ InitAsStandaloneIfNecessary();
+ CHECK(suppression_ctx);
+ const char *SuppType = ConvertTypeToFlagName(ET);
+ // Fast path: don't symbolize PC if there is no suppressions for given UB
+ // type.
+ if (!suppression_ctx->HasSuppressionType(SuppType))
+ return false;
+ Suppression *s = nullptr;
+ // Suppress by file name known to runtime.
+ if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
+ return true;
+ // Suppress by module name.
+ if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
+ if (suppression_ctx->Match(Module, SuppType, &s))
+ return true;
+ }
+ // Suppress by function or source file name from debug info.
+ SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
+ const AddressInfo &AI = Stack.get()->info;
+ return suppression_ctx->Match(AI.function, SuppType, &s) ||
+ suppression_ctx->Match(AI.file, SuppType, &s);
+}
+
#endif // CAN_SANITIZE_UB
diff --git a/libsanitizer/ubsan/ubsan_diag.h b/libsanitizer/ubsan/ubsan_diag.h
index 7103d522953..3456aaa72c5 100644
--- a/libsanitizer/ubsan/ubsan_diag.h
+++ b/libsanitizer/ubsan/ubsan_diag.h
@@ -209,23 +209,25 @@ public:
};
struct ReportOptions {
- /// If DieAfterReport is specified, UBSan will terminate the program after the
- /// report is printed.
- bool DieAfterReport;
+ // If FromUnrecoverableHandler is specified, UBSan runtime handler is not
+ // expected to return.
+ bool FromUnrecoverableHandler;
/// pc/bp are used to unwind the stack trace.
uptr pc;
uptr bp;
};
enum class ErrorType {
-#define UBSAN_CHECK(Name, SummaryKind, FlagName) Name,
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) Name,
#include "ubsan_checks.inc"
#undef UBSAN_CHECK
};
-#define GET_REPORT_OPTIONS(die_after_report) \
+bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET);
+
+#define GET_REPORT_OPTIONS(unrecoverable_handler) \
GET_CALLER_PC_BP; \
- ReportOptions Opts = {die_after_report, pc, bp}
+ ReportOptions Opts = {unrecoverable_handler, pc, bp}
/// \brief Instantiate this class before printing diagnostics in the error
/// report. This class ensures that reports from different threads and from
@@ -236,14 +238,15 @@ class ScopedReport {
ErrorType Type;
public:
- ScopedReport(ReportOptions Opts, Location SummaryLoc,
- ErrorType Type = ErrorType::GenericUB);
- void setErrorType(ErrorType T) { Type = T; }
+ ScopedReport(ReportOptions Opts, Location SummaryLoc, ErrorType Type);
~ScopedReport();
};
void InitializeSuppressions();
bool IsVptrCheckSuppressed(const char *TypeName);
+// Sometimes UBSan runtime can know filename from handlers arguments, even if
+// debug info is missing.
+bool IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename);
} // namespace __ubsan
diff --git a/libsanitizer/ubsan/ubsan_flags.cc b/libsanitizer/ubsan/ubsan_flags.cc
index 4e7db7a6a57..19f5de1727e 100644
--- a/libsanitizer/ubsan/ubsan_flags.cc
+++ b/libsanitizer/ubsan/ubsan_flags.cc
@@ -57,7 +57,7 @@ void InitializeFlags() {
parser.ParseString(MaybeCallUbsanDefaultOptions());
// Override from environment variable.
parser.ParseString(GetEnv("UBSAN_OPTIONS"));
- SetVerbosity(common_flags()->verbosity);
+ InitializeCommonFlags();
if (Verbosity()) ReportUnrecognizedFlags();
if (common_flags()->help) parser.PrintFlagDescriptions();
diff --git a/libsanitizer/ubsan/ubsan_handlers.cc b/libsanitizer/ubsan/ubsan_handlers.cc
index 8530fcffc88..0e343d32307 100644
--- a/libsanitizer/ubsan/ubsan_handlers.cc
+++ b/libsanitizer/ubsan/ubsan_handlers.cc
@@ -19,17 +19,20 @@
using namespace __sanitizer;
using namespace __ubsan;
-static bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
- // If source location is already acquired, we don't need to print an error
- // report for the second time. However, if we're in an unrecoverable handler,
- // it's possible that location was required by concurrently running thread.
- // In this case, we should continue the execution to ensure that any of
- // threads will grab the report mutex and print the report before
- // crashing the program.
- return SLoc.isDisabled() && !Opts.DieAfterReport;
+namespace __ubsan {
+bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) {
+ // We are not allowed to skip error report: if we are in unrecoverable
+ // handler, we have to terminate the program right now, and therefore
+ // have to print some diagnostic.
+ //
+ // Even if source location is disabled, it doesn't mean that we have
+ // already report an error to the user: some concurrently running
+ // thread could have acquired it, but not yet printed the report.
+ if (Opts.FromUnrecoverableHandler)
+ return false;
+ return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename());
}
-namespace __ubsan {
const char *TypeCheckKinds[] = {
"load of", "store to", "reference binding to", "member access within",
"member call on", "constructor call on", "downcast of", "downcast of",
@@ -39,8 +42,18 @@ const char *TypeCheckKinds[] = {
static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
ReportOptions Opts) {
Location Loc = Data->Loc.acquire();
- // Use the SourceLocation from Data to track deduplication, even if 'invalid'
- if (ignoreReport(Loc.getSourceLocation(), Opts))
+
+ ErrorType ET;
+ if (!Pointer)
+ ET = ErrorType::NullPointerUse;
+ else if (Data->Alignment && (Pointer & (Data->Alignment - 1)))
+ ET = ErrorType::MisalignedPointerUse;
+ else
+ ET = ErrorType::InsufficientObjectSize;
+
+ // Use the SourceLocation from Data to track deduplication, even if it's
+ // invalid.
+ if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
return;
SymbolizedStackHolder FallbackLoc;
@@ -49,24 +62,28 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
Loc = FallbackLoc;
}
- ScopedReport R(Opts, Loc);
+ ScopedReport R(Opts, Loc, ET);
- if (!Pointer) {
- R.setErrorType(ErrorType::NullPointerUse);
+ switch (ET) {
+ case ErrorType::NullPointerUse:
Diag(Loc, DL_Error, "%0 null pointer of type %1")
- << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
- } else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) {
- R.setErrorType(ErrorType::MisalignedPointerUse);
+ << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
+ break;
+ case ErrorType::MisalignedPointerUse:
Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, "
"which requires %2 byte alignment")
- << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer
- << Data->Alignment << Data->Type;
- } else {
- R.setErrorType(ErrorType::InsufficientObjectSize);
+ << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer
+ << Data->Alignment << Data->Type;
+ break;
+ case ErrorType::InsufficientObjectSize:
Diag(Loc, DL_Error, "%0 address %1 with insufficient space "
"for an object of type %2")
- << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+ << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type;
+ break;
+ default:
+ UNREACHABLE("unexpected error type!");
}
+
if (Pointer)
Diag(Pointer, DL_Note, "pointer points here");
}
@@ -89,12 +106,14 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
const char *Operator, T RHS,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ bool IsSigned = Data->Type.isSignedIntegerTy();
+ ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+ : ErrorType::UnsignedIntegerOverflow;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- bool IsSigned = Data->Type.isSignedIntegerTy();
- ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
- : ErrorType::UnsignedIntegerOverflow);
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error, "%0 integer overflow: "
"%1 %2 %3 cannot be represented in type %4")
@@ -102,12 +121,13 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
<< Value(Data->Type, LHS) << Operator << RHS << Data->Type;
}
-#define UBSAN_OVERFLOW_HANDLER(handler_name, op, abort) \
+#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \
void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \
ValueHandle RHS) { \
- GET_REPORT_OPTIONS(abort); \
+ GET_REPORT_OPTIONS(unrecoverable); \
handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \
- if (abort) Die(); \
+ if (unrecoverable) \
+ Die(); \
}
UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false)
@@ -120,12 +140,14 @@ UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true)
static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ bool IsSigned = Data->Type.isSignedIntegerTy();
+ ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+ : ErrorType::UnsignedIntegerOverflow;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- bool IsSigned = Data->Type.isSignedIntegerTy();
- ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
- : ErrorType::UnsignedIntegerOverflow);
+ ScopedReport R(Opts, Loc, ET);
if (IsSigned)
Diag(Loc, DL_Error,
@@ -152,22 +174,30 @@ void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
ValueHandle RHS, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ Value LHSVal(Data->Type, LHS);
+ Value RHSVal(Data->Type, RHS);
+
+ ErrorType ET;
+ if (RHSVal.isMinusOne())
+ ET = ErrorType::SignedIntegerOverflow;
+ else if (Data->Type.isIntegerTy())
+ ET = ErrorType::IntegerDivideByZero;
+ else
+ ET = ErrorType::FloatDivideByZero;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- ScopedReport R(Opts, Loc);
+ ScopedReport R(Opts, Loc, ET);
- Value LHSVal(Data->Type, LHS);
- Value RHSVal(Data->Type, RHS);
- if (RHSVal.isMinusOne()) {
- R.setErrorType(ErrorType::SignedIntegerOverflow);
- Diag(Loc, DL_Error,
- "division of %0 by -1 cannot be represented in type %1")
- << LHSVal << Data->Type;
- } else {
- R.setErrorType(Data->Type.isIntegerTy() ? ErrorType::IntegerDivideByZero
- : ErrorType::FloatDivideByZero);
+ switch (ET) {
+ case ErrorType::SignedIntegerOverflow:
+ Diag(Loc, DL_Error, "division of %0 by -1 cannot be represented in type %1")
+ << LHSVal << Data->Type;
+ break;
+ default:
Diag(Loc, DL_Error, "division by zero");
+ break;
}
}
@@ -188,29 +218,34 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
ValueHandle LHS, ValueHandle RHS,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ Value LHSVal(Data->LHSType, LHS);
+ Value RHSVal(Data->RHSType, RHS);
+
+ ErrorType ET;
+ if (RHSVal.isNegative() ||
+ RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
+ ET = ErrorType::InvalidShiftExponent;
+ else
+ ET = ErrorType::InvalidShiftBase;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- ScopedReport R(Opts, Loc);
+ ScopedReport R(Opts, Loc, ET);
- Value LHSVal(Data->LHSType, LHS);
- Value RHSVal(Data->RHSType, RHS);
- if (RHSVal.isNegative()) {
- R.setErrorType(ErrorType::InvalidShiftExponent);
- Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal;
- } else if (RHSVal.getPositiveIntValue() >=
- Data->LHSType.getIntegerBitWidth()) {
- R.setErrorType(ErrorType::InvalidShiftExponent);
- Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2")
- << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
- } else if (LHSVal.isNegative()) {
- R.setErrorType(ErrorType::InvalidShiftBase);
- Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal;
+ if (ET == ErrorType::InvalidShiftExponent) {
+ if (RHSVal.isNegative())
+ Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal;
+ else
+ Diag(Loc, DL_Error, "shift exponent %0 is too large for %1-bit type %2")
+ << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
} else {
- R.setErrorType(ErrorType::InvalidShiftBase);
- Diag(Loc, DL_Error,
- "left shift of %0 by %1 places cannot be represented in type %2")
- << LHSVal << RHSVal << Data->LHSType;
+ if (LHSVal.isNegative())
+ Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal;
+ else
+ Diag(Loc, DL_Error,
+ "left shift of %0 by %1 places cannot be represented in type %2")
+ << LHSVal << RHSVal << Data->LHSType;
}
}
@@ -232,10 +267,12 @@ void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ ErrorType ET = ErrorType::OutOfBoundsIndex;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- ScopedReport R(Opts, Loc, ErrorType::OutOfBoundsIndex);
+ ScopedReport R(Opts, Loc, ET);
Value IndexVal(Data->IndexType, Index);
Diag(Loc, DL_Error, "index %0 out of bounds for type %1")
@@ -282,10 +319,12 @@ void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ ErrorType ET = ErrorType::NonPositiveVLAIndex;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- ScopedReport R(Opts, Loc, ErrorType::NonPositiveVLAIndex);
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error, "variable length array bound evaluates to "
"non-positive value %0")
@@ -327,6 +366,7 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
SymbolizedStackHolder CallerLoc;
Location Loc;
const TypeDescriptor *FromType, *ToType;
+ ErrorType ET = ErrorType::FloatCastOverflow;
if (looksLikeFloatCastOverflowDataV1(DataPtr)) {
auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr);
@@ -337,14 +377,14 @@ static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
} else {
auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr);
SourceLocation SLoc = Data->Loc.acquire();
- if (ignoreReport(SLoc, Opts))
+ if (ignoreReport(SLoc, Opts, ET))
return;
Loc = SLoc;
FromType = &Data->FromType;
ToType = &Data->ToType;
}
- ScopedReport R(Opts, Loc, ErrorType::FloatCastOverflow);
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error,
"value %0 is outside the range of representable values of type %2")
@@ -365,14 +405,16 @@ void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data,
static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
- return;
-
// This check could be more precise if we used different handlers for
// -fsanitize=bool and -fsanitize=enum.
bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'"));
- ScopedReport R(Opts, Loc, IsBool ? ErrorType::InvalidBoolLoad
- : ErrorType::InvalidEnumLoad);
+ ErrorType ET =
+ IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error,
"load of value %0, which is not a valid value for type %1")
@@ -395,10 +437,12 @@ static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
ValueHandle Function,
ReportOptions Opts) {
SourceLocation CallLoc = Data->Loc.acquire();
- if (ignoreReport(CallLoc, Opts))
+ ErrorType ET = ErrorType::FunctionTypeMismatch;
+
+ if (ignoreReport(CallLoc, Opts, ET))
return;
- ScopedReport R(Opts, CallLoc, ErrorType::FunctionTypeMismatch);
+ ScopedReport R(Opts, CallLoc, ET);
SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
const char *FName = FLoc.get()->info.function;
@@ -427,10 +471,12 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort(
static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ ErrorType ET = ErrorType::InvalidNullReturn;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- ScopedReport R(Opts, Loc, ErrorType::InvalidNullReturn);
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error, "null pointer returned from function declared to never "
"return null");
@@ -451,10 +497,12 @@ void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ ErrorType ET = ErrorType::InvalidNullArgument;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- ScopedReport R(Opts, Loc, ErrorType::InvalidNullArgument);
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
"never be null") << Data->ArgIndex;
@@ -473,13 +521,18 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
Die();
}
-static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function,
+static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
ReportOptions Opts) {
+ if (Data->CheckKind != CFITCK_ICall)
+ Die();
+
SourceLocation Loc = Data->Loc.acquire();
- if (ignoreReport(Loc, Opts))
+ ErrorType ET = ErrorType::CFIBadType;
+
+ if (ignoreReport(Loc, Opts, ET))
return;
- ScopedReport R(Opts, Loc);
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
"indirect function call")
@@ -492,16 +545,37 @@ static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function,
Diag(FLoc, DL_Note, "%0 defined here") << FName;
}
-void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *Data,
- ValueHandle Function) {
+namespace __ubsan {
+#ifdef UBSAN_CAN_USE_CXXABI
+SANITIZER_WEAK_ATTRIBUTE
+void HandleCFIBadType(CFICheckFailData *Data, ValueHandle Vtable,
+ bool ValidVtable, ReportOptions Opts);
+#else
+static void HandleCFIBadType(CFICheckFailData *Data, ValueHandle Vtable,
+ bool ValidVtable, ReportOptions Opts) {
+ Die();
+}
+#endif
+} // namespace __ubsan
+
+void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data,
+ ValueHandle Value,
+ uptr ValidVtable) {
GET_REPORT_OPTIONS(false);
- handleCFIBadIcall(Data, Function, Opts);
+ if (Data->CheckKind == CFITCK_ICall)
+ handleCFIBadIcall(Data, Value, Opts);
+ else
+ HandleCFIBadType(Data, Value, ValidVtable, Opts);
}
-void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *Data,
- ValueHandle Function) {
+void __ubsan::__ubsan_handle_cfi_check_fail_abort(CFICheckFailData *Data,
+ ValueHandle Value,
+ uptr ValidVtable) {
GET_REPORT_OPTIONS(true);
- handleCFIBadIcall(Data, Function, Opts);
+ if (Data->CheckKind == CFITCK_ICall)
+ handleCFIBadIcall(Data, Value, Opts);
+ else
+ HandleCFIBadType(Data, Value, ValidVtable, Opts);
Die();
}
diff --git a/libsanitizer/ubsan/ubsan_handlers.h b/libsanitizer/ubsan/ubsan_handlers.h
index 668535868e9..ef741ca58ef 100644
--- a/libsanitizer/ubsan/ubsan_handlers.h
+++ b/libsanitizer/ubsan/ubsan_handlers.h
@@ -146,14 +146,25 @@ struct NonNullArgData {
/// \brief Handle passing null pointer to function with nonnull attribute.
RECOVERABLE(nonnull_arg, NonNullArgData *Data)
-struct CFIBadIcallData {
+/// \brief Known CFI check kinds.
+/// Keep in sync with the enum of the same name in CodeGenFunction.h
+enum CFITypeCheckKind : unsigned char {
+ CFITCK_VCall,
+ CFITCK_NVCall,
+ CFITCK_DerivedCast,
+ CFITCK_UnrelatedCast,
+ CFITCK_ICall,
+};
+
+struct CFICheckFailData {
+ CFITypeCheckKind CheckKind;
SourceLocation Loc;
const TypeDescriptor &Type;
};
-/// \brief Handle control flow integrity failure for indirect function calls.
-RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function)
-
+/// \brief Handle control flow integrity failures.
+RECOVERABLE(cfi_check_fail, CFICheckFailData *Data, ValueHandle Function,
+ uptr VtableIsValid)
}
#endif // UBSAN_HANDLERS_H
diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.cc b/libsanitizer/ubsan/ubsan_handlers_cxx.cc
index b50b4d4636d..015a9ffee02 100644
--- a/libsanitizer/ubsan/ubsan_handlers_cxx.cc
+++ b/libsanitizer/ubsan/ubsan_handlers_cxx.cc
@@ -13,6 +13,7 @@
#include "ubsan_platform.h"
#if CAN_SANITIZE_UB
+#include "ubsan_handlers.h"
#include "ubsan_handlers_cxx.h"
#include "ubsan_diag.h"
#include "ubsan_type_hash.h"
@@ -27,34 +28,42 @@ namespace __ubsan {
extern const char *TypeCheckKinds[];
}
-static void HandleDynamicTypeCacheMiss(
+// Returns true if UBSan has printed an error report.
+static bool HandleDynamicTypeCacheMiss(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
ReportOptions Opts) {
if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
// Just a cache miss. The type matches after all.
- return;
+ return false;
// Check if error report should be suppressed.
DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer);
if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName()))
- return;
+ return false;
SourceLocation Loc = Data->Loc.acquire();
- if (Loc.isDisabled())
- return;
+ ErrorType ET = ErrorType::DynamicTypeMismatch;
+ if (ignoreReport(Loc, Opts, ET))
+ return false;
- ScopedReport R(Opts, Loc, ErrorType::DynamicTypeMismatch);
+ ScopedReport R(Opts, Loc, ET);
Diag(Loc, DL_Error,
"%0 address %1 which does not point to an object of type %2")
<< TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
// If possible, say what type it actually points to.
- if (!DTI.isValid())
- Diag(Pointer, DL_Note, "object has invalid vptr")
- << TypeName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
- else if (!DTI.getOffset())
+ if (!DTI.isValid()) {
+ if (DTI.getOffset() < -VptrMaxOffsetToTop || DTI.getOffset() > VptrMaxOffsetToTop) {
+ Diag(Pointer, DL_Note, "object has a possibly invalid vptr: abs(offset to top) too big")
+ << TypeName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr");
+ } else {
+ Diag(Pointer, DL_Note, "object has invalid vptr")
+ << TypeName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
+ }
+ } else if (!DTI.getOffset())
Diag(Pointer, DL_Note, "object is of type %0")
<< TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
@@ -67,6 +76,7 @@ static void HandleDynamicTypeCacheMiss(
<< TypeName(DTI.getSubobjectTypeName())
<< Range(Pointer, Pointer + sizeof(uptr),
"vptr for %2 base class of %1");
+ return true;
}
void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
@@ -76,45 +86,60 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
}
void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
- GET_REPORT_OPTIONS(true);
- HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
+ // Note: -fsanitize=vptr is always recoverable.
+ GET_REPORT_OPTIONS(false);
+ if (HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts))
+ Die();
}
-static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable,
- ReportOptions Opts) {
+namespace __ubsan {
+void HandleCFIBadType(CFICheckFailData *Data, ValueHandle Vtable,
+ bool ValidVtable, ReportOptions Opts) {
SourceLocation Loc = Data->Loc.acquire();
- ScopedReport R(Opts, Loc, ErrorType::CFIBadType);
- DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable);
+ ErrorType ET = ErrorType::CFIBadType;
- static const char *TypeCheckKinds[] = {
- "virtual call",
- "non-virtual call",
- "base-to-derived cast",
- "cast to unrelated type",
- };
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+ DynamicTypeInfo DTI = ValidVtable
+ ? getDynamicTypeInfoFromVtable((void *)Vtable)
+ : DynamicTypeInfo(0, 0, 0);
+
+ const char *CheckKindStr;
+ switch (Data->CheckKind) {
+ case CFITCK_VCall:
+ CheckKindStr = "virtual call";
+ break;
+ case CFITCK_NVCall:
+ CheckKindStr = "non-virtual call";
+ break;
+ case CFITCK_DerivedCast:
+ CheckKindStr = "base-to-derived cast";
+ break;
+ case CFITCK_UnrelatedCast:
+ CheckKindStr = "cast to unrelated type";
+ break;
+ case CFITCK_ICall:
+ Die();
+ }
Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
"%1 (vtable address %2)")
- << Data->Type << TypeCheckKinds[Data->TypeCheckKind] << (void *)Vtable;
+ << Data->Type << CheckKindStr << (void *)Vtable;
// If possible, say what type it actually points to.
- if (!DTI.isValid())
- Diag(Vtable, DL_Note, "invalid vtable");
- else
+ if (!DTI.isValid()) {
+ const char *module = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable);
+ if (module)
+ Diag(Vtable, DL_Note, "invalid vtable in module %0") << module;
+ else
+ Diag(Vtable, DL_Note, "invalid vtable");
+ } else {
Diag(Vtable, DL_Note, "vtable is of type %0")
<< TypeName(DTI.getMostDerivedTypeName());
+ }
}
+} // namespace __ubsan
-void __ubsan::__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data,
- ValueHandle Vtable) {
- GET_REPORT_OPTIONS(false);
- HandleCFIBadType(Data, Vtable, Opts);
-}
-
-void __ubsan::__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data,
- ValueHandle Vtable) {
- GET_REPORT_OPTIONS(true);
- HandleCFIBadType(Data, Vtable, Opts);
-}
-
-#endif // CAN_SANITIZE_UB
+#endif // CAN_SANITIZE_UB
diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.h b/libsanitizer/ubsan/ubsan_handlers_cxx.h
index b1486864298..37382359b1e 100644
--- a/libsanitizer/ubsan/ubsan_handlers_cxx.h
+++ b/libsanitizer/ubsan/ubsan_handlers_cxx.h
@@ -23,12 +23,6 @@ struct DynamicTypeCacheMissData {
unsigned char TypeCheckKind;
};
-struct CFIBadTypeData {
- SourceLocation Loc;
- const TypeDescriptor &Type;
- unsigned char TypeCheckKind;
-};
-
/// \brief Handle a runtime type check failure, caused by an incorrect vptr.
/// When this handler is called, all we know is that the type was not in the
/// cache; this does not necessarily imply the existence of a bug.
@@ -38,14 +32,6 @@ void __ubsan_handle_dynamic_type_cache_miss(
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
-
-/// \brief Handle a control flow integrity check failure by printing a
-/// diagnostic.
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
-__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data, ValueHandle Vtable);
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
-__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data, ValueHandle Vtable);
-
}
#endif // UBSAN_HANDLERS_H
diff --git a/libsanitizer/ubsan/ubsan_init.cc b/libsanitizer/ubsan/ubsan_init.cc
index 5da4122c07a..07f748109e7 100644
--- a/libsanitizer/ubsan/ubsan_init.cc
+++ b/libsanitizer/ubsan/ubsan_init.cc
@@ -37,6 +37,7 @@ static void CommonStandaloneInit() {
InitializeFlags();
CacheBinaryName();
__sanitizer_set_report_path(common_flags()->log_path);
+ AndroidLogInit();
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
CommonInit();
ubsan_mode = UBSAN_MODE_STANDALONE;
diff --git a/libsanitizer/ubsan/ubsan_platform.h b/libsanitizer/ubsan/ubsan_platform.h
index 999bf815493..fa4e1a191aa 100644
--- a/libsanitizer/ubsan/ubsan_platform.h
+++ b/libsanitizer/ubsan/ubsan_platform.h
@@ -15,7 +15,8 @@
// Other platforms should be easy to add, and probably work as-is.
#if (defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)) && \
(defined(__x86_64__) || defined(__i386__) || defined(__arm__) || \
- defined(__aarch64__) || defined(__mips__) || defined(__powerpc64__))
+ defined(__aarch64__) || defined(__mips__) || defined(__powerpc64__) || \
+ defined(__s390__))
# define CAN_SANITIZE_UB 1
#elif defined(_WIN32)
# define CAN_SANITIZE_UB 1
diff --git a/libsanitizer/ubsan/ubsan_type_hash.h b/libsanitizer/ubsan/ubsan_type_hash.h
index 2da070edffa..610fcb44ea7 100644
--- a/libsanitizer/ubsan/ubsan_type_hash.h
+++ b/libsanitizer/ubsan/ubsan_type_hash.h
@@ -51,6 +51,10 @@ bool checkDynamicType(void *Object, void *Type, HashValue Hash);
const unsigned VptrTypeCacheSize = 128;
+/// A sanity check for Vtable. Offsets to top must be reasonably small
+/// numbers (by absolute value). It's a weak check for Vtable corruption.
+const int VptrMaxOffsetToTop = 1<<20;
+
/// \brief A cache of the results of checkDynamicType. \c checkDynamicType would
/// return \c true (modulo hash collisions) if
/// \code
diff --git a/libsanitizer/ubsan/ubsan_type_hash_itanium.cc b/libsanitizer/ubsan/ubsan_type_hash_itanium.cc
index e4f133434d6..790b126815d 100644
--- a/libsanitizer/ubsan/ubsan_type_hash_itanium.cc
+++ b/libsanitizer/ubsan/ubsan_type_hash_itanium.cc
@@ -71,6 +71,8 @@ public:
namespace abi = __cxxabiv1;
+using namespace __sanitizer;
+
// We implement a simple two-level cache for type-checking results. For each
// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
// unique; if it collides, we will get false negatives, but:
@@ -113,7 +115,9 @@ static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
const abi::__class_type_info *Base,
sptr Offset) {
- if (Derived->__type_name == Base->__type_name)
+ if (Derived->__type_name == Base->__type_name ||
+ (SANITIZER_NON_UNIQUE_TYPEINFO &&
+ !internal_strcmp(Derived->__type_name, Base->__type_name)))
return Offset == 0;
if (const abi::__si_class_type_info *SI =
@@ -161,7 +165,7 @@ static const abi::__class_type_info *findBaseAtOffset(
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
if (!VTI)
// No base class subobjects.
- return 0;
+ return nullptr;
for (unsigned int base = 0; base != VTI->base_count; ++base) {
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
@@ -176,7 +180,7 @@ static const abi::__class_type_info *findBaseAtOffset(
return Base;
}
- return 0;
+ return nullptr;
}
namespace {
@@ -192,11 +196,11 @@ struct VtablePrefix {
VtablePrefix *getVtablePrefix(void *Vtable) {
VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
if (!Vptr)
- return 0;
+ return nullptr;
VtablePrefix *Prefix = Vptr - 1;
if (!Prefix->TypeInfo)
// This can't possibly be a valid vtable.
- return 0;
+ return nullptr;
return Prefix;
}
@@ -217,6 +221,10 @@ bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
if (!Vtable)
return false;
+ if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop) {
+ // Too large or too small offset are signs of Vtable corruption.
+ return false;
+ }
// Check that this is actually a type_info object for a class type.
abi::__class_type_info *Derived =
@@ -238,7 +246,9 @@ __ubsan::DynamicTypeInfo
__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
if (!Vtable)
- return DynamicTypeInfo(0, 0, 0);
+ return DynamicTypeInfo(nullptr, 0, nullptr);
+ if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop)
+ return DynamicTypeInfo(nullptr, Vtable->Offset, nullptr);
const abi::__class_type_info *ObjectType = findBaseAtOffset(
static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
-Vtable->Offset);
diff --git a/libsanitizer/ubsan/ubsan_value.cc b/libsanitizer/ubsan/ubsan_value.cc
index e327f6ffc2e..3e158f92e07 100644
--- a/libsanitizer/ubsan/ubsan_value.cc
+++ b/libsanitizer/ubsan/ubsan_value.cc
@@ -81,12 +81,12 @@ FloatMax Value::getFloatValue() const {
#endif
case 32: {
float Value;
-#if defined(__BIG_ENDIAN__)
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
// For big endian the float value is in the last 4 bytes.
// On some targets we may only have 4 bytes so we count backwards from
// the end of Val to account for both the 32-bit and 64-bit cases.
internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4);
-#else
+#else
internal_memcpy(&Value, &Val, 4);
#endif
return Value;