diff options
author | Maxim Ostapenko <m.ostapenko@samsung.com> | 2016-11-08 22:04:09 +0000 |
---|---|---|
committer | Maxim Ostapenko <chefmax@gcc.gnu.org> | 2016-11-09 00:04:09 +0200 |
commit | 1018981977de9f2056cdfcd8173458e85a3751f2 (patch) | |
tree | 9c1a8b279416b5f379d7631c1b7f36ab18797212 /libsanitizer/ubsan | |
parent | f31d9224e6346c775648139ae3f5acf3b70582e0 (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.in | 1 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_checks.inc | 56 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_diag.cc | 229 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_diag.h | 21 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_flags.cc | 2 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_handlers.cc | 254 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_handlers.h | 19 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_handlers_cxx.cc | 105 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_handlers_cxx.h | 14 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_init.cc | 1 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_platform.h | 3 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_type_hash.h | 4 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_type_hash_itanium.cc | 22 | ||||
-rw-r--r-- | libsanitizer/ubsan/ubsan_value.cc | 4 |
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; |