diff options
author | chefmax <chefmax@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-10-21 07:32:45 +0000 |
---|---|---|
committer | chefmax <chefmax@138bc75d-0d04-0410-961f-82ee72b054a4> | 2015-10-21 07:32:45 +0000 |
commit | 5645a48f7ebd0f97a072b7a2eb40b27cea9d4318 (patch) | |
tree | 2bdaf703dd35e1806b59bd7d74c7eee290a1054f /libsanitizer/ubsan | |
parent | 397881d34f32eddf4a6665789f1a7cdd5ff3695e (diff) | |
download | gcc-5645a48f7ebd0f97a072b7a2eb40b27cea9d4318.tar.gz |
libsanitizer merge from upstream r250806.
libsanitizer/
2015-10-20 Maxim Ostapenko <m.ostapenko@partner.samsung.com>
* All source files: Merge from upstream r250806.
* configure.ac (link_sanitizer_common): Add -lrt flag.
* configure.tgt: Enable TSAN and LSAN for aarch64-linux targets.
Set CXX_ABI_NEEDED=true for darwin.
* asan/Makefile.am (asan_files): Add new files.
(DEFS): Add DCAN_SANITIZE_UB=0 and remove unused and legacy
DASAN_FLEXIBLE_MAPPING_AND_OFFSET=0.
* asan/Makefile.in: Regenerate.
* ubsan/Makefile.am (ubsan_files): Add new files.
(DEFS): Add DCAN_SANITIZE_UB=1.
(libubsan_la_LIBADD): Add -lc++abi if CXX_ABI_NEEDED is true.
* ubsan/Makefile.in: Regenerate.
* tsan/Makefile.am (tsan_files): Add new files.
(DEFS): Add DCAN_SANITIZE_UB=0.
* tsan/Makefile.in: Regenerate.
* sanitizer_common/Makefile.am (sanitizer_common_files): Add new files.
* sanitizer_common/Makefile.in: Regenerate.
* asan/libtool-version: Bump the libasan SONAME.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@229111 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libsanitizer/ubsan')
22 files changed, 1007 insertions, 458 deletions
diff --git a/libsanitizer/ubsan/Makefile.am b/libsanitizer/ubsan/Makefile.am index 12d1ffac8f5..29b4115389f 100644 --- a/libsanitizer/ubsan/Makefile.am +++ b/libsanitizer/ubsan/Makefile.am @@ -3,7 +3,7 @@ AM_CPPFLAGS = -I $(top_srcdir) -I $(top_srcdir)/include # May be used by toolexeclibdir. gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER) -DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC -DCAN_SANITIZE_UB=1 AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS) AM_CXXFLAGS += -std=gnu++11 @@ -11,15 +11,21 @@ ACLOCAL_AMFLAGS = -I m4 toolexeclib_LTLIBRARIES = libubsan.la -ubsan_files = \ +ubsan_plugin_files = \ ubsan_diag.cc \ ubsan_flags.cc \ ubsan_handlers.cc \ ubsan_handlers_cxx.cc \ ubsan_init.cc \ ubsan_type_hash.cc \ + ubsan_type_hash_itanium.cc \ + ubsan_type_hash_win.cc \ ubsan_value.cc +ubsan_files = \ + $(ubsan_plugin_files) \ + ubsan_init_standalone.cc + libubsan_la_SOURCES = $(ubsan_files) libubsan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la if !USING_MAC_INTERPOSE @@ -29,10 +35,13 @@ if LIBBACKTRACE_SUPPORTED libubsan_la_LIBADD += $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la endif libubsan_la_LIBADD += $(LIBSTDCXX_RAW_CXX_LDFLAGS) +if USE_CXX_ABI_FLAG +libubsan_la_LIBADD += -lc++abi +endif libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libubsan) # Use special rules for files that require RTTI support. -ubsan_handlers_cxx.% ubsan_type_hash.% : AM_CXXFLAGS += -frtti +ubsan_handlers_cxx.% ubsan_type_hash.% ubsan_type_hash_itanium.% : AM_CXXFLAGS += -frtti # Work around what appears to be a GNU make bug handling MAKEFLAGS # values defined in terms of make variables, as is the case for CC and diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in index 64aad19a59f..3b5cfbf3778 100644 --- a/libsanitizer/ubsan/Makefile.in +++ b/libsanitizer/ubsan/Makefile.in @@ -54,6 +54,7 @@ host_triplet = @host@ target_triplet = @target@ @USING_MAC_INTERPOSE_FALSE@am__append_1 = $(top_builddir)/interception/libinterception.la @LIBBACKTRACE_SUPPORTED_TRUE@am__append_2 = $(top_builddir)/libbacktrace/libsanitizer_libbacktrace.la +@USE_CXX_ABI_FLAG_TRUE@am__append_3 = -lc++abi subdir = ubsan DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -106,11 +107,14 @@ LTLIBRARIES = $(toolexeclib_LTLIBRARIES) am__DEPENDENCIES_1 = libubsan_la_DEPENDENCIES = \ $(top_builddir)/sanitizer_common/libsanitizer_common.la \ - $(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1) + $(am__append_1) $(am__append_2) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) am__objects_1 = ubsan_diag.lo ubsan_flags.lo ubsan_handlers.lo \ ubsan_handlers_cxx.lo ubsan_init.lo ubsan_type_hash.lo \ + ubsan_type_hash_itanium.lo ubsan_type_hash_win.lo \ ubsan_value.lo -am_libubsan_la_OBJECTS = $(am__objects_1) +am__objects_2 = $(am__objects_1) ubsan_init_standalone.lo +am_libubsan_la_OBJECTS = $(am__objects_2) libubsan_la_OBJECTS = $(am_libubsan_la_OBJECTS) libubsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ @@ -160,7 +164,7 @@ CXXCPP = @CXXCPP@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ -DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC +DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC -DCAN_SANITIZE_UB=1 DEPDIR = @DEPDIR@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ @@ -290,19 +294,26 @@ AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \ -std=gnu++11 ACLOCAL_AMFLAGS = -I m4 toolexeclib_LTLIBRARIES = libubsan.la -ubsan_files = \ +ubsan_plugin_files = \ ubsan_diag.cc \ ubsan_flags.cc \ ubsan_handlers.cc \ ubsan_handlers_cxx.cc \ ubsan_init.cc \ ubsan_type_hash.cc \ + ubsan_type_hash_itanium.cc \ + ubsan_type_hash_win.cc \ ubsan_value.cc +ubsan_files = \ + $(ubsan_plugin_files) \ + ubsan_init_standalone.cc + libubsan_la_SOURCES = $(ubsan_files) libubsan_la_LIBADD = \ $(top_builddir)/sanitizer_common/libsanitizer_common.la \ - $(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS) + $(am__append_1) $(am__append_2) $(LIBSTDCXX_RAW_CXX_LDFLAGS) \ + $(am__append_3) libubsan_la_LDFLAGS = -version-info `grep -v '^\#' $(srcdir)/libtool-version` $(link_libubsan) # Work around what appears to be a GNU make bug handling MAKEFLAGS @@ -424,7 +435,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers_cxx.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_init.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_init_standalone.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_itanium.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_win.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_value.Plo@am__quote@ .cc.o: @@ -631,7 +645,7 @@ uninstall-am: uninstall-toolexeclibLTLIBRARIES # Use special rules for files that require RTTI support. -ubsan_handlers_cxx.% ubsan_type_hash.% : AM_CXXFLAGS += -frtti +ubsan_handlers_cxx.% ubsan_type_hash.% ubsan_type_hash_itanium.% : AM_CXXFLAGS += -frtti # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/libsanitizer/ubsan/ubsan_checks.inc b/libsanitizer/ubsan/ubsan_checks.inc new file mode 100644 index 00000000000..935d82c5079 --- /dev/null +++ b/libsanitizer/ubsan/ubsan_checks.inc @@ -0,0 +1,51 @@ +//===-- ubsan_checks.inc ----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// List of checks handled by UBSan runtime. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_CHECK +# error "Define UBSAN_CHECK prior to including this file!" +#endif + +// UBSAN_CHECK(Name, SummaryKind, FlagName) +// SummaryKind and FlagName 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(SignedIntegerOverflow, "signed-integer-overflow", + "-fsanitize=signed-integer-overflow") +UBSAN_CHECK(UnsignedIntegerOverflow, "unsigned-integer-overflow", + "-fsanitize=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") +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") diff --git a/libsanitizer/ubsan/ubsan_diag.cc b/libsanitizer/ubsan/ubsan_diag.cc index 028ac1a9b97..1197f837f75 100644 --- a/libsanitizer/ubsan/ubsan_diag.cc +++ b/libsanitizer/ubsan/ubsan_diag.cc @@ -9,19 +9,23 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_diag.h" #include "ubsan_init.h" #include "ubsan_flags.h" +#include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stacktrace_printer.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include <stdio.h> using namespace __ubsan; static void MaybePrintStackTrace(uptr pc, uptr bp) { - // We assume that flags are already parsed: InitIfNecessary + // We assume that flags are already parsed, as UBSan runtime // will definitely be called when we print the first diagnostics message. if (!flags()->print_stacktrace) return; @@ -37,20 +41,41 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) { stack.Print(); } -static void MaybeReportErrorSummary(Location Loc) { +static const char *ConvertTypeToString(ErrorType Type) { + switch (Type) { +#define UBSAN_CHECK(Name, SummaryKind, FlagName) \ + case ErrorType::Name: \ + return SummaryKind; +#include "ubsan_checks.inc" +#undef UBSAN_CHECK + } + UNREACHABLE("unknown ErrorType!"); +} + +static void MaybeReportErrorSummary(Location Loc, ErrorType Type) { if (!common_flags()->print_summary) return; - // Don't try to unwind the stack trace in UBSan summaries: just use the - // provided location. + if (!flags()->report_error_type) + Type = ErrorType::GenericUB; + const char *ErrorKind = ConvertTypeToString(Type); if (Loc.isSourceLocation()) { SourceLocation SLoc = Loc.getSourceLocation(); if (!SLoc.isInvalid()) { - ReportErrorSummary("undefined-behavior", SLoc.getFilename(), - SLoc.getLine(), ""); + AddressInfo AI; + AI.file = internal_strdup(SLoc.getFilename()); + AI.line = SLoc.getLine(); + AI.column = SLoc.getColumn(); + AI.function = internal_strdup(""); // Avoid printing ?? as function name. + ReportErrorSummary(ErrorKind, AI); + AI.Clear(); return; } + } else if (Loc.isSymbolizedStack()) { + const AddressInfo &AI = Loc.getSymbolizedStack()->info; + ReportErrorSummary(ErrorKind, AI); + return; } - ReportErrorSummary("undefined-behavior"); + ReportErrorSummary(ErrorKind); } namespace { @@ -64,31 +89,9 @@ class Decorator : public SanitizerCommonDecorator { }; } -Location __ubsan::getCallerLocation(uptr CallerLoc) { - if (!CallerLoc) - return Location(); - - uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); - return getFunctionLocation(Loc, 0); -} - -Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) { - if (!Loc) - return Location(); - InitIfNecessary(); - - AddressInfo Info; - if (!Symbolizer::GetOrInit()->SymbolizePC(Loc, &Info, 1) || !Info.module || - !*Info.module) - return Location(Loc); - - if (FName && Info.function) - *FName = Info.function; - - if (!Info.file) - return ModuleLocation(Info.module, Info.module_offset); - - return SourceLocation(Info.file, Info.line, Info.column); +SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) { + InitAsStandaloneIfNecessary(); + return Symbolizer::GetOrInit()->SymbolizePC(PC); } Diag &Diag::operator<<(const TypeDescriptor &V) { @@ -129,18 +132,27 @@ static void renderLocation(Location Loc) { LocBuffer.append("<unknown>"); else RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), - SLoc.getColumn(), common_flags()->strip_path_prefix); - break; - } - case Location::LK_Module: { - ModuleLocation MLoc = Loc.getModuleLocation(); - RenderModuleLocation(&LocBuffer, MLoc.getModuleName(), MLoc.getOffset(), - common_flags()->strip_path_prefix); + SLoc.getColumn(), common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); break; } case Location::LK_Memory: LocBuffer.append("%p", Loc.getMemoryLocation()); break; + case Location::LK_Symbolized: { + const AddressInfo &Info = Loc.getSymbolizedStack()->info; + if (Info.file) { + RenderSourceLocation(&LocBuffer, 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, + common_flags()->strip_path_prefix); + } else { + LocBuffer.append("%p", Info.address); + } + break; + } case Location::LK_Null: LocBuffer.append("<unknown>"); break; @@ -164,8 +176,12 @@ static void renderText(const char *Message, const Diag::Arg *Args) { case Diag::AK_String: Printf("%s", A.String); break; - case Diag::AK_Mangled: { - Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); + 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: @@ -185,7 +201,11 @@ static void renderText(const char *Message, const Diag::Arg *Args) { // FIXME: Support floating-point formatting in sanitizer_common's // printf, and stop using snprintf here. char Buffer[32]; +#if SANITIZER_WINDOWS + sprintf_s(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float); +#else snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float); +#endif Printf("%s", Buffer); break; } @@ -332,25 +352,38 @@ Diag::~Diag() { NumRanges, Args); } -ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc) - : Opts(Opts), SummaryLoc(SummaryLoc) { - InitIfNecessary(); +ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc, + ErrorType Type) + : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) { + InitAsStandaloneIfNecessary(); CommonSanitizerReportMutex.Lock(); } ScopedReport::~ScopedReport() { MaybePrintStackTrace(Opts.pc, Opts.bp); - MaybeReportErrorSummary(SummaryLoc); + MaybeReportErrorSummary(SummaryLoc, Type); CommonSanitizerReportMutex.Unlock(); if (Opts.DieAfterReport || flags()->halt_on_error) Die(); } -bool __ubsan::MatchSuppression(const char *Str, SuppressionType Type) { +ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +static SuppressionContext *suppression_ctx = nullptr; +static const char kVptrCheck[] = "vptr_check"; +static const char *kSuppressionTypes[] = { kVptrCheck }; + +void __ubsan::InitializeSuppressions() { + CHECK_EQ(nullptr, suppression_ctx); + suppression_ctx = new (suppression_placeholder) // NOLINT + SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); + suppression_ctx->ParseFromFile(flags()->suppressions); +} + +bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) { + InitAsStandaloneIfNecessary(); + CHECK(suppression_ctx); Suppression *s; - // If .preinit_array is not used, it is possible that the UBSan runtime is not - // initialized. - if (!SANITIZER_CAN_USE_PREINIT_ARRAY) - InitIfNecessary(); - return SuppressionContext::Get()->Match(Str, Type, &s); + return suppression_ctx->Match(TypeName, kVptrCheck, &s); } + +#endif // CAN_SANITIZE_UB diff --git a/libsanitizer/ubsan/ubsan_diag.h b/libsanitizer/ubsan/ubsan_diag.h index fbb3f1ff9d4..7103d522953 100644 --- a/libsanitizer/ubsan/ubsan_diag.h +++ b/libsanitizer/ubsan/ubsan_diag.h @@ -13,79 +13,84 @@ #include "ubsan_value.h" #include "sanitizer_common/sanitizer_stacktrace.h" -#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_symbolizer.h" namespace __ubsan { -/// \brief A location within a loaded module in the program. These are used when -/// the location can't be resolved to a SourceLocation. -class ModuleLocation { - const char *ModuleName; - uptr Offset; +class SymbolizedStackHolder { + SymbolizedStack *Stack; + + void clear() { + if (Stack) + Stack->ClearAll(); + } public: - ModuleLocation() : ModuleName(0), Offset(0) {} - ModuleLocation(const char *ModuleName, uptr Offset) - : ModuleName(ModuleName), Offset(Offset) {} - const char *getModuleName() const { return ModuleName; } - uptr getOffset() const { return Offset; } + explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr) + : Stack(Stack) {} + ~SymbolizedStackHolder() { clear(); } + void reset(SymbolizedStack *S) { + if (Stack != S) + clear(); + Stack = S; + } + const SymbolizedStack *get() const { return Stack; } }; +SymbolizedStack *getSymbolizedLocation(uptr PC); + +inline SymbolizedStack *getCallerLocation(uptr CallerPC) { + CHECK(CallerPC); + uptr PC = StackTrace::GetPreviousInstructionPc(CallerPC); + return getSymbolizedLocation(PC); +} + /// A location of some data within the program's address space. typedef uptr MemoryLocation; /// \brief Location at which a diagnostic can be emitted. Either a -/// SourceLocation, a ModuleLocation, or a MemoryLocation. +/// SourceLocation, a MemoryLocation, or a SymbolizedStack. class Location { public: - enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory }; + enum LocationKind { LK_Null, LK_Source, LK_Memory, LK_Symbolized }; private: LocationKind Kind; // FIXME: In C++11, wrap these in an anonymous union. SourceLocation SourceLoc; - ModuleLocation ModuleLoc; MemoryLocation MemoryLoc; + const SymbolizedStack *SymbolizedLoc; // Not owned. public: Location() : Kind(LK_Null) {} Location(SourceLocation Loc) : Kind(LK_Source), SourceLoc(Loc) {} - Location(ModuleLocation Loc) : - Kind(LK_Module), ModuleLoc(Loc) {} Location(MemoryLocation Loc) : Kind(LK_Memory), MemoryLoc(Loc) {} + // SymbolizedStackHolder must outlive Location object. + Location(const SymbolizedStackHolder &Stack) : + Kind(LK_Symbolized), SymbolizedLoc(Stack.get()) {} LocationKind getKind() const { return Kind; } bool isSourceLocation() const { return Kind == LK_Source; } - bool isModuleLocation() const { return Kind == LK_Module; } bool isMemoryLocation() const { return Kind == LK_Memory; } + bool isSymbolizedStack() const { return Kind == LK_Symbolized; } SourceLocation getSourceLocation() const { CHECK(isSourceLocation()); return SourceLoc; } - ModuleLocation getModuleLocation() const { - CHECK(isModuleLocation()); - return ModuleLoc; - } MemoryLocation getMemoryLocation() const { CHECK(isMemoryLocation()); return MemoryLoc; } + const SymbolizedStack *getSymbolizedStack() const { + CHECK(isSymbolizedStack()); + return SymbolizedLoc; + } }; -/// Try to obtain a location for the caller. This might fail, and produce either -/// an invalid location or a module location for the caller. -Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC()); - -/// Try to obtain a location for the given function pointer. This might fail, -/// and produce either an invalid location or a module location for the caller. -/// If FName is non-null and the name of the function is known, set *FName to -/// the function name, otherwise *FName is unchanged. -Location getFunctionLocation(uptr Loc, const char **FName); - /// A diagnostic severity level. enum DiagLevel { DL_Error, ///< An error. @@ -106,11 +111,11 @@ public: const char *getText() const { return Text; } }; -/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'. -class MangledName { +/// \brief A C++ type name. Really just a strong typedef for 'const char*'. +class TypeName { const char *Name; public: - MangledName(const char *Name) : Name(Name) {} + TypeName(const char *Name) : Name(Name) {} const char *getName() const { return Name; } }; @@ -134,7 +139,7 @@ public: /// Kinds of arguments, corresponding to members of \c Arg's union. enum ArgKind { AK_String, ///< A string argument, displayed as-is. - AK_Mangled,///< A C++ mangled name, demangled before display. + AK_TypeName,///< A C++ type name, possibly demangled before display. AK_UInt, ///< An unsigned integer argument. AK_SInt, ///< A signed integer argument. AK_Float, ///< A floating-point argument. @@ -145,7 +150,7 @@ public: struct Arg { Arg() {} Arg(const char *String) : Kind(AK_String), String(String) {} - Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {} + Arg(TypeName TN) : Kind(AK_TypeName), String(TN.getName()) {} Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {} Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {} Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {} @@ -195,7 +200,7 @@ public: ~Diag(); Diag &operator<<(const char *Str) { return AddArg(Str); } - Diag &operator<<(MangledName MN) { return AddArg(MN); } + Diag &operator<<(TypeName TN) { return AddArg(TN); } Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); } Diag &operator<<(const void *V) { return AddArg(V); } Diag &operator<<(const TypeDescriptor &V); @@ -212,6 +217,12 @@ struct ReportOptions { uptr bp; }; +enum class ErrorType { +#define UBSAN_CHECK(Name, SummaryKind, FlagName) Name, +#include "ubsan_checks.inc" +#undef UBSAN_CHECK +}; + #define GET_REPORT_OPTIONS(die_after_report) \ GET_CALLER_PC_BP; \ ReportOptions Opts = {die_after_report, pc, bp} @@ -222,13 +233,17 @@ struct ReportOptions { class ScopedReport { ReportOptions Opts; Location SummaryLoc; + ErrorType Type; public: - ScopedReport(ReportOptions Opts, Location SummaryLoc); + ScopedReport(ReportOptions Opts, Location SummaryLoc, + ErrorType Type = ErrorType::GenericUB); + void setErrorType(ErrorType T) { Type = T; } ~ScopedReport(); }; -bool MatchSuppression(const char *Str, SuppressionType Type); +void InitializeSuppressions(); +bool IsVptrCheckSuppressed(const char *TypeName); } // namespace __ubsan diff --git a/libsanitizer/ubsan/ubsan_flags.cc b/libsanitizer/ubsan/ubsan_flags.cc index 9645a857260..4e7db7a6a57 100644 --- a/libsanitizer/ubsan/ubsan_flags.cc +++ b/libsanitizer/ubsan/ubsan_flags.cc @@ -9,53 +9,78 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" namespace __ubsan { -static const char *MaybeCallUbsanDefaultOptions() { +const char *MaybeCallUbsanDefaultOptions() { return (&__ubsan_default_options) ? __ubsan_default_options() : ""; } -void InitializeCommonFlags() { - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - cf->print_summary = false; - // Override from user-specified string. - ParseCommonFlagsFromString(cf, MaybeCallUbsanDefaultOptions()); - // Override from environment variable. - ParseCommonFlagsFromString(cf, GetEnv("UBSAN_OPTIONS")); -} - Flags ubsan_flags; -static void ParseFlagsFromString(Flags *f, const char *str) { - if (!str) - return; - ParseFlag(str, &f->halt_on_error, "halt_on_error", - "Crash the program after printing the first error report"); - ParseFlag(str, &f->print_stacktrace, "print_stacktrace", - "Include full stacktrace into an error report"); +void Flags::SetDefaults() { +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "ubsan_flags.inc" +#undef UBSAN_FLAG +} + +void RegisterUbsanFlags(FlagParser *parser, Flags *f) { +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "ubsan_flags.inc" +#undef UBSAN_FLAG } void InitializeFlags() { + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.print_summary = false; + OverrideCommonFlags(cf); + } + Flags *f = flags(); - // Default values. - f->halt_on_error = false; - f->print_stacktrace = false; + f->SetDefaults(); + + FlagParser parser; + RegisterCommonFlags(&parser); + RegisterUbsanFlags(&parser, f); + // Override from user-specified string. - ParseFlagsFromString(f, MaybeCallUbsanDefaultOptions()); + parser.ParseString(MaybeCallUbsanDefaultOptions()); // Override from environment variable. - ParseFlagsFromString(f, GetEnv("UBSAN_OPTIONS")); + parser.ParseString(GetEnv("UBSAN_OPTIONS")); + SetVerbosity(common_flags()->verbosity); + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); } } // namespace __ubsan -#if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *__ubsan_default_options() { return ""; } -} // extern "C" #endif + +#if SANITIZER_WINDOWS +const char *__ubsan_default_default_options() { return ""; } +# ifdef _WIN64 +# pragma comment(linker, "/alternatename:__ubsan_default_options=__ubsan_default_default_options") +# else +# pragma comment(linker, "/alternatename:___ubsan_default_options=___ubsan_default_default_options") +# endif +#endif + +} // extern "C" + +#endif // CAN_SANITIZE_UB diff --git a/libsanitizer/ubsan/ubsan_flags.h b/libsanitizer/ubsan/ubsan_flags.h index 00be9b004f4..2604b6b00cc 100644 --- a/libsanitizer/ubsan/ubsan_flags.h +++ b/libsanitizer/ubsan/ubsan_flags.h @@ -13,18 +13,27 @@ #include "sanitizer_common/sanitizer_internal_defs.h" +namespace __sanitizer { +class FlagParser; +} + namespace __ubsan { struct Flags { - bool halt_on_error; - bool print_stacktrace; +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "ubsan_flags.inc" +#undef UBSAN_FLAG + + void SetDefaults(); }; extern Flags ubsan_flags; inline Flags *flags() { return &ubsan_flags; } -void InitializeCommonFlags(); void InitializeFlags(); +void RegisterUbsanFlags(FlagParser *parser, Flags *f); + +const char *MaybeCallUbsanDefaultOptions(); } // namespace __ubsan diff --git a/libsanitizer/ubsan/ubsan_flags.inc b/libsanitizer/ubsan/ubsan_flags.inc new file mode 100644 index 00000000000..170777adf26 --- /dev/null +++ b/libsanitizer/ubsan/ubsan_flags.inc @@ -0,0 +1,24 @@ +//===-- ubsan_flags.inc -----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UBSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_FLAG +# error "Define UBSAN_FLAG prior to including this file!" +#endif + +// UBSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +UBSAN_FLAG(bool, halt_on_error, false, + "Crash the program after printing the first error report") +UBSAN_FLAG(bool, print_stacktrace, false, + "Include full stacktrace into an error report") +UBSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") +UBSAN_FLAG(bool, report_error_type, false, + "Print specific error type instead of 'undefined-behavior' in summary.") diff --git a/libsanitizer/ubsan/ubsan_handlers.cc b/libsanitizer/ubsan/ubsan_handlers.cc index 770d3c28a4a..8530fcffc88 100644 --- a/libsanitizer/ubsan/ubsan_handlers.cc +++ b/libsanitizer/ubsan/ubsan_handlers.cc @@ -9,6 +9,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_handlers.h" #include "ubsan_diag.h" @@ -35,29 +37,36 @@ const char *TypeCheckKinds[] = { } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, - Location FallbackLoc, ReportOptions Opts) { + ReportOptions Opts) { Location Loc = Data->Loc.acquire(); // Use the SourceLocation from Data to track deduplication, even if 'invalid' if (ignoreReport(Loc.getSourceLocation(), Opts)) return; - if (Data->Loc.isInvalid()) + SymbolizedStackHolder FallbackLoc; + if (Data->Loc.isInvalid()) { + FallbackLoc.reset(getCallerLocation(Opts.pc)); Loc = FallbackLoc; + } ScopedReport R(Opts, Loc); - if (!Pointer) + if (!Pointer) { + R.setErrorType(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))) + } else if (Data->Alignment && (Pointer & (Data->Alignment - 1))) { + R.setErrorType(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 + } else { + R.setErrorType(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; + } if (Pointer) Diag(Pointer, DL_Note, "pointer points here"); } @@ -65,12 +74,12 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data, ValueHandle Pointer) { GET_REPORT_OPTIONS(false); - handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts); + handleTypeMismatchImpl(Data, Pointer, Opts); } void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data, ValueHandle Pointer) { GET_REPORT_OPTIONS(true); - handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts); + handleTypeMismatchImpl(Data, Pointer, Opts); Die(); } @@ -83,11 +92,13 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS, if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + bool IsSigned = Data->Type.isSignedIntegerTy(); + ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow + : ErrorType::UnsignedIntegerOverflow); Diag(Loc, DL_Error, "%0 integer overflow: " "%1 %2 %3 cannot be represented in type %4") - << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned") + << (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS) << Operator << RHS << Data->Type; } @@ -112,17 +123,18 @@ static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal, if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + bool IsSigned = Data->Type.isSignedIntegerTy(); + ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow + : ErrorType::UnsignedIntegerOverflow); - if (Data->Type.isSignedIntegerTy()) + if (IsSigned) Diag(Loc, DL_Error, "negation of %0 cannot be represented in type %1; " "cast to an unsigned type to negate this value to itself") - << Value(Data->Type, OldVal) << Data->Type; + << Value(Data->Type, OldVal) << Data->Type; else - Diag(Loc, DL_Error, - "negation of %0 cannot be represented in type %1") - << Value(Data->Type, OldVal) << Data->Type; + Diag(Loc, DL_Error, "negation of %0 cannot be represented in type %1") + << Value(Data->Type, OldVal) << Data->Type; } void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data, @@ -147,12 +159,16 @@ static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS, Value LHSVal(Data->Type, LHS); Value RHSVal(Data->Type, RHS); - if (RHSVal.isMinusOne()) + 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 + } else { + R.setErrorType(Data->Type.isIntegerTy() ? ErrorType::IntegerDivideByZero + : ErrorType::FloatDivideByZero); Diag(Loc, DL_Error, "division by zero"); + } } void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data, @@ -179,18 +195,23 @@ static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data, Value LHSVal(Data->LHSType, LHS); Value RHSVal(Data->RHSType, RHS); - if (RHSVal.isNegative()) + if (RHSVal.isNegative()) { + R.setErrorType(ErrorType::InvalidShiftExponent); Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal; - else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth()) - 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()) + } 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; - else + } 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; + << LHSVal << RHSVal << Data->LHSType; + } } void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data, @@ -214,7 +235,7 @@ static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index, if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::OutOfBoundsIndex); Value IndexVal(Data->IndexType, Index); Diag(Loc, DL_Error, "index %0 out of bounds for type %1") @@ -235,7 +256,7 @@ void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data, static void handleBuiltinUnreachableImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc); + ScopedReport R(Opts, Data->Loc, ErrorType::UnreachableCall); Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call"); } @@ -246,7 +267,7 @@ void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) { } static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) { - ScopedReport R(Opts, Data->Loc); + ScopedReport R(Opts, Data->Loc, ErrorType::MissingReturn); Diag(Data->Loc, DL_Error, "execution reached the end of a value-returning function " "without returning a value"); @@ -264,7 +285,7 @@ static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound, if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::NonPositiveVLAIndex); Diag(Loc, DL_Error, "variable length array bound evaluates to " "non-positive value %0") @@ -283,25 +304,59 @@ void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data, Die(); } -static void handleFloatCastOverflow(FloatCastOverflowData *Data, - ValueHandle From, ReportOptions Opts) { - // TODO: Add deduplication once a SourceLocation is generated for this check. - Location Loc = getCallerLocation(); - ScopedReport R(Opts, Loc); +static bool looksLikeFloatCastOverflowDataV1(void *Data) { + // First field is either a pointer to filename or a pointer to a + // TypeDescriptor. + u8 *FilenameOrTypeDescriptor; + internal_memcpy(&FilenameOrTypeDescriptor, Data, + sizeof(FilenameOrTypeDescriptor)); + + // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer + // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known, + // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename, + // adding two printable characters will not yield such a value. Otherwise, + // if one of them is 0xff, this is most likely TK_Unknown type descriptor. + u16 MaybeFromTypeKind = + FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1]; + return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == 0xff || + FilenameOrTypeDescriptor[1] == 0xff; +} + +static void handleFloatCastOverflow(void *DataPtr, ValueHandle From, + ReportOptions Opts) { + SymbolizedStackHolder CallerLoc; + Location Loc; + const TypeDescriptor *FromType, *ToType; + + if (looksLikeFloatCastOverflowDataV1(DataPtr)) { + auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr); + CallerLoc.reset(getCallerLocation(Opts.pc)); + Loc = CallerLoc; + FromType = &Data->FromType; + ToType = &Data->ToType; + } else { + auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr); + SourceLocation SLoc = Data->Loc.acquire(); + if (ignoreReport(SLoc, Opts)) + return; + Loc = SLoc; + FromType = &Data->FromType; + ToType = &Data->ToType; + } + + ScopedReport R(Opts, Loc, ErrorType::FloatCastOverflow); Diag(Loc, DL_Error, "value %0 is outside the range of representable values of type %2") - << Value(Data->FromType, From) << Data->FromType << Data->ToType; + << Value(*FromType, From) << *FromType << *ToType; } -void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data, - ValueHandle From) { +void __ubsan::__ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) { GET_REPORT_OPTIONS(false); handleFloatCastOverflow(Data, From, Opts); } -void -__ubsan::__ubsan_handle_float_cast_overflow_abort(FloatCastOverflowData *Data, - ValueHandle From) { +void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data, + ValueHandle From) { GET_REPORT_OPTIONS(true); handleFloatCastOverflow(Data, From, Opts); Die(); @@ -313,7 +368,11 @@ static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val, if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + // 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); Diag(Loc, DL_Error, "load of value %0, which is not a valid value for type %1") @@ -335,16 +394,21 @@ void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data, ValueHandle Function, ReportOptions Opts) { - const char *FName = "(unknown)"; + SourceLocation CallLoc = Data->Loc.acquire(); + if (ignoreReport(CallLoc, Opts)) + return; - Location Loc = getFunctionLocation(Function, &FName); + ScopedReport R(Opts, CallLoc, ErrorType::FunctionTypeMismatch); - ScopedReport R(Opts, Loc); + SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); + const char *FName = FLoc.get()->info.function; + if (!FName) + FName = "(unknown)"; - Diag(Data->Loc, DL_Error, + Diag(CallLoc, DL_Error, "call to function %0 through pointer to incorrect function type %1") - << FName << Data->Type; - Diag(Loc, DL_Note, "%0 defined here") << FName; + << FName << Data->Type; + Diag(FLoc, DL_Note, "%0 defined here") << FName; } void @@ -366,7 +430,7 @@ static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) { if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::InvalidNullReturn); Diag(Loc, DL_Error, "null pointer returned from function declared to never " "return null"); @@ -390,7 +454,7 @@ static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) { if (ignoreReport(Loc, Opts)) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::InvalidNullArgument); Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to " "never be null") << Data->ArgIndex; @@ -408,3 +472,37 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) { handleNonNullArg(Data, Opts); Die(); } + +static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function, + ReportOptions Opts) { + SourceLocation Loc = Data->Loc.acquire(); + if (ignoreReport(Loc, Opts)) + return; + + ScopedReport R(Opts, Loc); + + Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during " + "indirect function call") + << Data->Type; + + SymbolizedStackHolder FLoc(getSymbolizedLocation(Function)); + const char *FName = FLoc.get()->info.function; + if (!FName) + FName = "(unknown)"; + Diag(FLoc, DL_Note, "%0 defined here") << FName; +} + +void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *Data, + ValueHandle Function) { + GET_REPORT_OPTIONS(false); + handleCFIBadIcall(Data, Function, Opts); +} + +void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *Data, + ValueHandle Function) { + GET_REPORT_OPTIONS(true); + handleCFIBadIcall(Data, Function, Opts); + Die(); +} + +#endif // CAN_SANITIZE_UB diff --git a/libsanitizer/ubsan/ubsan_handlers.h b/libsanitizer/ubsan/ubsan_handlers.h index 92365d81893..668535868e9 100644 --- a/libsanitizer/ubsan/ubsan_handlers.h +++ b/libsanitizer/ubsan/ubsan_handlers.h @@ -95,14 +95,22 @@ struct VLABoundData { /// \brief Handle a VLA with a non-positive bound. RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound) +// Keeping this around for binary compatibility with (sanitized) programs +// compiled with older compilers. struct FloatCastOverflowData { - // FIXME: SourceLocation Loc; const TypeDescriptor &FromType; const TypeDescriptor &ToType; }; -/// \brief Handle overflow in a conversion to or from a floating-point type. -RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From) +struct FloatCastOverflowDataV2 { + SourceLocation Loc; + const TypeDescriptor &FromType; + const TypeDescriptor &ToType; +}; + +/// Handle overflow in a conversion to or from a floating-point type. +/// void *Data is one of FloatCastOverflowData* or FloatCastOverflowDataV2* +RECOVERABLE(float_cast_overflow, void *Data, ValueHandle From) struct InvalidValueData { SourceLocation Loc; @@ -138,6 +146,14 @@ struct NonNullArgData { /// \brief Handle passing null pointer to function with nonnull attribute. RECOVERABLE(nonnull_arg, NonNullArgData *Data) +struct CFIBadIcallData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +/// \brief Handle control flow integrity failure for indirect function calls. +RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function) + } #endif // UBSAN_HANDLERS_H diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.cc b/libsanitizer/ubsan/ubsan_handlers_cxx.cc index 86f3e57a8bc..b50b4d4636d 100644 --- a/libsanitizer/ubsan/ubsan_handlers_cxx.cc +++ b/libsanitizer/ubsan/ubsan_handlers_cxx.cc @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_handlers_cxx.h" #include "ubsan_diag.h" #include "ubsan_type_hash.h" @@ -33,16 +35,15 @@ static void HandleDynamicTypeCacheMiss( return; // Check if error report should be suppressed. - DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); - if (DTI.isValid() && - MatchSuppression(DTI.getMostDerivedTypeName(), SuppressionVptrCheck)) + DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer); + if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName())) return; SourceLocation Loc = Data->Loc.acquire(); if (Loc.isDisabled()) return; - ScopedReport R(Opts, Loc); + ScopedReport R(Opts, Loc, ErrorType::DynamicTypeMismatch); Diag(Loc, DL_Error, "%0 address %1 which does not point to an object of type %2") @@ -51,19 +52,19 @@ static void HandleDynamicTypeCacheMiss( // If possible, say what type it actually points to. if (!DTI.isValid()) Diag(Pointer, DL_Note, "object has invalid vptr") - << MangledName(DTI.getMostDerivedTypeName()) + << TypeName(DTI.getMostDerivedTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); else if (!DTI.getOffset()) Diag(Pointer, DL_Note, "object is of type %0") - << MangledName(DTI.getMostDerivedTypeName()) + << TypeName(DTI.getMostDerivedTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0"); else // FIXME: Find the type at the specified offset, and include that // in the note. Diag(Pointer - DTI.getOffset(), DL_Note, "object is base class subobject at offset %0 within object of type %1") - << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName()) - << MangledName(DTI.getSubobjectTypeName()) + << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName()) + << TypeName(DTI.getSubobjectTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1"); } @@ -78,3 +79,42 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( GET_REPORT_OPTIONS(true); HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts); } + +static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable, + ReportOptions Opts) { + SourceLocation Loc = Data->Loc.acquire(); + ScopedReport R(Opts, Loc, ErrorType::CFIBadType); + DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable); + + static const char *TypeCheckKinds[] = { + "virtual call", + "non-virtual call", + "base-to-derived cast", + "cast to unrelated type", + }; + + Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during " + "%1 (vtable address %2)") + << Data->Type << TypeCheckKinds[Data->TypeCheckKind] << (void *)Vtable; + + // If possible, say what type it actually points to. + if (!DTI.isValid()) + Diag(Vtable, DL_Note, "invalid vtable"); + else + Diag(Vtable, DL_Note, "vtable is of type %0") + << TypeName(DTI.getMostDerivedTypeName()); +} + +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 diff --git a/libsanitizer/ubsan/ubsan_handlers_cxx.h b/libsanitizer/ubsan/ubsan_handlers_cxx.h index 3419744e390..b1486864298 100644 --- a/libsanitizer/ubsan/ubsan_handlers_cxx.h +++ b/libsanitizer/ubsan/ubsan_handlers_cxx.h @@ -23,6 +23,12 @@ 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. @@ -33,6 +39,13 @@ 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 f28fc811f50..5da4122c07a 100644 --- a/libsanitizer/ubsan/ubsan_init.cc +++ b/libsanitizer/ubsan/ubsan_init.cc @@ -9,51 +9,68 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB +#include "ubsan_diag.h" #include "ubsan_init.h" #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" -#include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_symbolizer.h" using namespace __ubsan; -static bool ubsan_inited; +static enum { + UBSAN_MODE_UNKNOWN = 0, + UBSAN_MODE_STANDALONE, + UBSAN_MODE_PLUGIN +} ubsan_mode; +static StaticSpinMutex ubsan_init_mu; -void __ubsan::InitIfNecessary() { -#if !SANITIZER_CAN_USE_PREINIT_ARRAY - // No need to lock mutex if we're initializing from preinit array. - static StaticSpinMutex init_mu; - SpinMutexLock l(&init_mu); -#endif - if (LIKELY(ubsan_inited)) - return; - if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) { - // WARNING: If this condition holds, then either UBSan runs in a standalone - // mode, or initializer for another sanitizer hasn't run yet. In a latter - // case, another sanitizer will overwrite "SanitizerToolName" and reparse - // common flags. It means, that we are not allowed to *use* common flags - // in this function. - SanitizerToolName = "UndefinedBehaviorSanitizer"; - InitializeCommonFlags(); - } - // Initialize UBSan-specific flags. +static void CommonInit() { + InitializeSuppressions(); +} + +static void CommonStandaloneInit() { + SanitizerToolName = "UndefinedBehaviorSanitizer"; InitializeFlags(); - SuppressionContext::InitIfNecessary(); - ubsan_inited = true; + CacheBinaryName(); + __sanitizer_set_report_path(common_flags()->log_path); + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); + CommonInit(); + ubsan_mode = UBSAN_MODE_STANDALONE; +} + +void __ubsan::InitAsStandalone() { + if (SANITIZER_CAN_USE_PREINIT_ARRAY) { + CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode); + CommonStandaloneInit(); + return; + } + SpinMutexLock l(&ubsan_init_mu); + CHECK_NE(UBSAN_MODE_PLUGIN, ubsan_mode); + if (ubsan_mode == UBSAN_MODE_UNKNOWN) + CommonStandaloneInit(); } -#if SANITIZER_CAN_USE_PREINIT_ARRAY -__attribute__((section(".preinit_array"), used)) -void (*__local_ubsan_preinit)(void) = __ubsan::InitIfNecessary; -#else -// Use a dynamic initializer. -class UbsanInitializer { - public: - UbsanInitializer() { - InitIfNecessary(); +void __ubsan::InitAsStandaloneIfNecessary() { + if (SANITIZER_CAN_USE_PREINIT_ARRAY) { + CHECK_NE(UBSAN_MODE_UNKNOWN, ubsan_mode); + return; } -}; -static UbsanInitializer ubsan_initializer; -#endif // SANITIZER_CAN_USE_PREINIT_ARRAY + SpinMutexLock l(&ubsan_init_mu); + if (ubsan_mode == UBSAN_MODE_UNKNOWN) + CommonStandaloneInit(); +} + +void __ubsan::InitAsPlugin() { +#if !SANITIZER_CAN_USE_PREINIT_ARRAY + SpinMutexLock l(&ubsan_init_mu); +#endif + CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode); + CommonInit(); + ubsan_mode = UBSAN_MODE_PLUGIN; +} + +#endif // CAN_SANITIZE_UB diff --git a/libsanitizer/ubsan/ubsan_init.h b/libsanitizer/ubsan/ubsan_init.h index b76bbfe7bb7..6a8366f82bf 100644 --- a/libsanitizer/ubsan/ubsan_init.h +++ b/libsanitizer/ubsan/ubsan_init.h @@ -13,9 +13,16 @@ namespace __ubsan { -// NOTE: This function might take a lock (if .preinit_array initialization is -// not used). It's generally a bad idea to call it on a fast path. -void InitIfNecessary(); +// Initialize UBSan as a standalone tool. Typically should be called early +// during initialization. +void InitAsStandalone(); + +// Initialize UBSan as a standalone tool, if it hasn't been initialized before. +void InitAsStandaloneIfNecessary(); + +// Initializes UBSan as a plugin tool. This function should be called once +// from "parent tool" (e.g. ASan) initialization. +void InitAsPlugin(); } // namespace __ubsan diff --git a/libsanitizer/ubsan/ubsan_init_standalone.cc b/libsanitizer/ubsan/ubsan_init_standalone.cc new file mode 100644 index 00000000000..1630fd724fb --- /dev/null +++ b/libsanitizer/ubsan/ubsan_init_standalone.cc @@ -0,0 +1,32 @@ +//===-- ubsan_init_standalone.cc ------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Initialization of standalone UBSan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_platform.h" +#if !CAN_SANITIZE_UB +# error "UBSan is not supported on this platform!" +#endif + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "ubsan_init.h" + +#if SANITIZER_CAN_USE_PREINIT_ARRAY +__attribute__((section(".preinit_array"), used)) +void (*__local_ubsan_preinit)(void) = __ubsan::InitAsStandalone; +#else +// Use a dynamic initializer. +class UbsanStandaloneInitializer { + public: + UbsanStandaloneInitializer() { + __ubsan::InitAsStandalone(); + } +}; +static UbsanStandaloneInitializer ubsan_standalone_initializer; +#endif // SANITIZER_CAN_USE_PREINIT_ARRAY diff --git a/libsanitizer/ubsan/ubsan_platform.h b/libsanitizer/ubsan/ubsan_platform.h new file mode 100644 index 00000000000..999bf815493 --- /dev/null +++ b/libsanitizer/ubsan/ubsan_platform.h @@ -0,0 +1,27 @@ +//===-- ubsan_platform.h ----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the platforms which UBSan is supported at. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_PLATFORM_H +#define UBSAN_PLATFORM_H + +#ifndef CAN_SANITIZE_UB +// 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__)) +# define CAN_SANITIZE_UB 1 +#elif defined(_WIN32) +# define CAN_SANITIZE_UB 1 +#else +# define CAN_SANITIZE_UB 0 +#endif +#endif //CAN_SANITIZE_UB + +#endif diff --git a/libsanitizer/ubsan/ubsan_type_hash.cc b/libsanitizer/ubsan/ubsan_type_hash.cc index 5eab1f561f2..65160aa4aba 100644 --- a/libsanitizer/ubsan/ubsan_type_hash.cc +++ b/libsanitizer/ubsan/ubsan_type_hash.cc @@ -9,240 +9,24 @@ // relationships. This file is only linked into C++ compilations, and is // permitted to use language features which require a C++ ABI library. // +// Most of the implementation lives in an ABI-specific source file +// (ubsan_type_hash_{itanium,win}.cc). +// //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_type_hash.h" #include "sanitizer_common/sanitizer_common.h" -// The following are intended to be binary compatible with the definitions -// given in the Itanium ABI. We make no attempt to be ODR-compatible with -// those definitions, since existing ABI implementations aren't. - -namespace std { - class type_info { - public: - virtual ~type_info(); - - const char *__type_name; - }; -} - -namespace __cxxabiv1 { - -/// Type info for classes with no bases, and base class for type info for -/// classes with bases. -class __class_type_info : public std::type_info { - virtual ~__class_type_info(); -}; - -/// Type info for classes with simple single public inheritance. -class __si_class_type_info : public __class_type_info { -public: - virtual ~__si_class_type_info(); - - const __class_type_info *__base_type; -}; - -class __base_class_type_info { -public: - const __class_type_info *__base_type; - long __offset_flags; - - enum __offset_flags_masks { - __virtual_mask = 0x1, - __public_mask = 0x2, - __offset_shift = 8 - }; -}; - -/// Type info for classes with multiple, virtual, or non-public inheritance. -class __vmi_class_type_info : public __class_type_info { -public: - virtual ~__vmi_class_type_info(); - - unsigned int flags; - unsigned int base_count; - __base_class_type_info base_info[1]; -}; - -} - -namespace abi = __cxxabiv1; - -// 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: -// * such a collision would have to occur on the *first* bad access, -// * the probability of such a collision is low (and for a 64-bit target, is -// negligible), and -// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs -// give better coverage. -// -// The first caching layer is a small hash table with no chaining; buckets are -// reused as needed. The second caching layer is a large hash table with open -// chaining. We can freely evict from either layer since this is just a cache. -// -// FIXME: Make these hash table accesses thread-safe. The races here are benign: -// assuming the unsequenced loads and stores don't misbehave too badly, -// the worst case is false negatives or poor cache behavior, not false -// positives or crashes. - -/// Find a bucket to store the given hash value in. -static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { - static const unsigned HashTableSize = 65537; - static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; - - unsigned First = (V & 65535) ^ 1; - unsigned Probe = First; - for (int Tries = 5; Tries; --Tries) { - if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) - return &__ubsan_vptr_hash_set[Probe]; - Probe += ((V >> 16) & 65535) + 1; - if (Probe >= HashTableSize) - Probe -= HashTableSize; - } - // FIXME: Pick a random entry from the probe sequence to evict rather than - // just taking the first. - return &__ubsan_vptr_hash_set[First]; -} - /// A cache of recently-checked hashes. Mini hash table with "random" evictions. __ubsan::HashValue __ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize]; -/// \brief Determine whether \p Derived has a \p Base base class subobject at -/// offset \p Offset. -static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, - const abi::__class_type_info *Base, - sptr Offset) { - if (Derived->__type_name == Base->__type_name) - return Offset == 0; - - if (const abi::__si_class_type_info *SI = - dynamic_cast<const abi::__si_class_type_info*>(Derived)) - return isDerivedFromAtOffset(SI->__base_type, Base, Offset); - - const abi::__vmi_class_type_info *VTI = - dynamic_cast<const abi::__vmi_class_type_info*>(Derived); - if (!VTI) - // No base class subobjects. - return false; - - // Look for a base class which is derived from \p Base at the right offset. - for (unsigned int base = 0; base != VTI->base_count; ++base) { - // FIXME: Curtail the recursion if this base can't possibly contain the - // given offset. - sptr OffsetHere = VTI->base_info[base].__offset_flags >> - abi::__base_class_type_info::__offset_shift; - if (VTI->base_info[base].__offset_flags & - abi::__base_class_type_info::__virtual_mask) - // For now, just punt on virtual bases and say 'yes'. - // FIXME: OffsetHere is the offset in the vtable of the virtual base - // offset. Read the vbase offset out of the vtable and use it. - return true; - if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, - Base, Offset - OffsetHere)) - return true; - } - - return false; -} - -/// \brief Find the derived-most dynamic base class of \p Derived at offset -/// \p Offset. -static const abi::__class_type_info *findBaseAtOffset( - const abi::__class_type_info *Derived, sptr Offset) { - if (!Offset) - return Derived; - - if (const abi::__si_class_type_info *SI = - dynamic_cast<const abi::__si_class_type_info*>(Derived)) - return findBaseAtOffset(SI->__base_type, Offset); - - const abi::__vmi_class_type_info *VTI = - dynamic_cast<const abi::__vmi_class_type_info*>(Derived); - if (!VTI) - // No base class subobjects. - return 0; - - for (unsigned int base = 0; base != VTI->base_count; ++base) { - sptr OffsetHere = VTI->base_info[base].__offset_flags >> - abi::__base_class_type_info::__offset_shift; - if (VTI->base_info[base].__offset_flags & - abi::__base_class_type_info::__virtual_mask) - // FIXME: Can't handle virtual bases yet. - continue; - if (const abi::__class_type_info *Base = - findBaseAtOffset(VTI->base_info[base].__base_type, - Offset - OffsetHere)) - return Base; - } - - return 0; -} - -namespace { - -struct VtablePrefix { - /// The offset from the vptr to the start of the most-derived object. - /// This will only be greater than zero in some virtual base class vtables - /// used during object con-/destruction, and will usually be exactly zero. - sptr Offset; - /// The type_info object describing the most-derived class type. - std::type_info *TypeInfo; -}; -VtablePrefix *getVtablePrefix(void *Object) { - VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object); - if (!*VptrPtr) - return 0; - VtablePrefix *Prefix = *VptrPtr - 1; - if (!Prefix->TypeInfo) - // This can't possibly be a valid vtable. - return 0; - return Prefix; -} - +__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromObject(void *Object) { + void *VtablePtr = *reinterpret_cast<void **>(Object); + return getDynamicTypeInfoFromVtable(VtablePtr); } -bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { - // A crash anywhere within this function probably means the vptr is corrupted. - // FIXME: Perform these checks more cautiously. - - // Check whether this is something we've evicted from the cache. - HashValue *Bucket = getTypeCacheHashTableBucket(Hash); - if (*Bucket == Hash) { - __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; - return true; - } - - VtablePrefix *Vtable = getVtablePrefix(Object); - if (!Vtable) - return false; - - // Check that this is actually a type_info object for a class type. - abi::__class_type_info *Derived = - dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo); - if (!Derived) - return false; - - abi::__class_type_info *Base = (abi::__class_type_info*)Type; - if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) - return false; - - // Success. Cache this result. - __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; - *Bucket = Hash; - return true; -} - -__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) { - VtablePrefix *Vtable = getVtablePrefix(Object); - if (!Vtable) - return DynamicTypeInfo(0, 0, 0); - const abi::__class_type_info *ObjectType = findBaseAtOffset( - static_cast<const abi::__class_type_info*>(Vtable->TypeInfo), - -Vtable->Offset); - return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, - ObjectType ? ObjectType->__type_name : "<unknown>"); -} +#endif // CAN_SANITIZE_UB diff --git a/libsanitizer/ubsan/ubsan_type_hash.h b/libsanitizer/ubsan/ubsan_type_hash.h index 138559f204d..2da070edffa 100644 --- a/libsanitizer/ubsan/ubsan_type_hash.h +++ b/libsanitizer/ubsan/ubsan_type_hash.h @@ -39,7 +39,10 @@ public: }; /// \brief Get information about the dynamic type of an object. -DynamicTypeInfo getDynamicTypeInfo(void *Object); +DynamicTypeInfo getDynamicTypeInfoFromObject(void *Object); + +/// \brief Get information about the dynamic type of an object from its vtable. +DynamicTypeInfo getDynamicTypeInfoFromVtable(void *Vtable); /// \brief Check whether the dynamic type of \p Object has a \p Type subobject /// at offset 0. diff --git a/libsanitizer/ubsan/ubsan_type_hash_itanium.cc b/libsanitizer/ubsan/ubsan_type_hash_itanium.cc new file mode 100644 index 00000000000..e4f133434d6 --- /dev/null +++ b/libsanitizer/ubsan/ubsan_type_hash_itanium.cc @@ -0,0 +1,249 @@ +//===-- ubsan_type_hash_itanium.cc ----------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of type hashing/lookup for Itanium C++ ABI. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB && !SANITIZER_WINDOWS +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +// The following are intended to be binary compatible with the definitions +// given in the Itanium ABI. We make no attempt to be ODR-compatible with +// those definitions, since existing ABI implementations aren't. + +namespace std { + class type_info { + public: + virtual ~type_info(); + + const char *__type_name; + }; +} + +namespace __cxxabiv1 { + +/// Type info for classes with no bases, and base class for type info for +/// classes with bases. +class __class_type_info : public std::type_info { + ~__class_type_info() override; +}; + +/// Type info for classes with simple single public inheritance. +class __si_class_type_info : public __class_type_info { +public: + ~__si_class_type_info() override; + + const __class_type_info *__base_type; +}; + +class __base_class_type_info { +public: + const __class_type_info *__base_type; + long __offset_flags; + + enum __offset_flags_masks { + __virtual_mask = 0x1, + __public_mask = 0x2, + __offset_shift = 8 + }; +}; + +/// Type info for classes with multiple, virtual, or non-public inheritance. +class __vmi_class_type_info : public __class_type_info { +public: + ~__vmi_class_type_info() override; + + unsigned int flags; + unsigned int base_count; + __base_class_type_info base_info[1]; +}; + +} + +namespace abi = __cxxabiv1; + +// 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: +// * such a collision would have to occur on the *first* bad access, +// * the probability of such a collision is low (and for a 64-bit target, is +// negligible), and +// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs +// give better coverage. +// +// The first caching layer is a small hash table with no chaining; buckets are +// reused as needed. The second caching layer is a large hash table with open +// chaining. We can freely evict from either layer since this is just a cache. +// +// FIXME: Make these hash table accesses thread-safe. The races here are benign: +// assuming the unsequenced loads and stores don't misbehave too badly, +// the worst case is false negatives or poor cache behavior, not false +// positives or crashes. + +/// Find a bucket to store the given hash value in. +static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { + static const unsigned HashTableSize = 65537; + static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; + + unsigned First = (V & 65535) ^ 1; + unsigned Probe = First; + for (int Tries = 5; Tries; --Tries) { + if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) + return &__ubsan_vptr_hash_set[Probe]; + Probe += ((V >> 16) & 65535) + 1; + if (Probe >= HashTableSize) + Probe -= HashTableSize; + } + // FIXME: Pick a random entry from the probe sequence to evict rather than + // just taking the first. + return &__ubsan_vptr_hash_set[First]; +} + +/// \brief Determine whether \p Derived has a \p Base base class subobject at +/// offset \p Offset. +static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, + const abi::__class_type_info *Base, + sptr Offset) { + if (Derived->__type_name == Base->__type_name) + return Offset == 0; + + if (const abi::__si_class_type_info *SI = + dynamic_cast<const abi::__si_class_type_info*>(Derived)) + return isDerivedFromAtOffset(SI->__base_type, Base, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast<const abi::__vmi_class_type_info*>(Derived); + if (!VTI) + // No base class subobjects. + return false; + + // Look for a base class which is derived from \p Base at the right offset. + for (unsigned int base = 0; base != VTI->base_count; ++base) { + // FIXME: Curtail the recursion if this base can't possibly contain the + // given offset. + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // For now, just punt on virtual bases and say 'yes'. + // FIXME: OffsetHere is the offset in the vtable of the virtual base + // offset. Read the vbase offset out of the vtable and use it. + return true; + if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, + Base, Offset - OffsetHere)) + return true; + } + + return false; +} + +/// \brief Find the derived-most dynamic base class of \p Derived at offset +/// \p Offset. +static const abi::__class_type_info *findBaseAtOffset( + const abi::__class_type_info *Derived, sptr Offset) { + if (!Offset) + return Derived; + + if (const abi::__si_class_type_info *SI = + dynamic_cast<const abi::__si_class_type_info*>(Derived)) + return findBaseAtOffset(SI->__base_type, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast<const abi::__vmi_class_type_info*>(Derived); + if (!VTI) + // No base class subobjects. + return 0; + + for (unsigned int base = 0; base != VTI->base_count; ++base) { + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // FIXME: Can't handle virtual bases yet. + continue; + if (const abi::__class_type_info *Base = + findBaseAtOffset(VTI->base_info[base].__base_type, + Offset - OffsetHere)) + return Base; + } + + return 0; +} + +namespace { + +struct VtablePrefix { + /// The offset from the vptr to the start of the most-derived object. + /// This will only be greater than zero in some virtual base class vtables + /// used during object con-/destruction, and will usually be exactly zero. + sptr Offset; + /// The type_info object describing the most-derived class type. + std::type_info *TypeInfo; +}; +VtablePrefix *getVtablePrefix(void *Vtable) { + VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable); + if (!Vptr) + return 0; + VtablePrefix *Prefix = Vptr - 1; + if (!Prefix->TypeInfo) + // This can't possibly be a valid vtable. + return 0; + return Prefix; +} + +} + +bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { + // A crash anywhere within this function probably means the vptr is corrupted. + // FIXME: Perform these checks more cautiously. + + // Check whether this is something we've evicted from the cache. + HashValue *Bucket = getTypeCacheHashTableBucket(Hash); + if (*Bucket == Hash) { + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + return true; + } + + void *VtablePtr = *reinterpret_cast<void **>(Object); + VtablePrefix *Vtable = getVtablePrefix(VtablePtr); + if (!Vtable) + return false; + + // Check that this is actually a type_info object for a class type. + abi::__class_type_info *Derived = + dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo); + if (!Derived) + return false; + + abi::__class_type_info *Base = (abi::__class_type_info*)Type; + if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) + return false; + + // Success. Cache this result. + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + *Bucket = Hash; + return true; +} + +__ubsan::DynamicTypeInfo +__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) { + VtablePrefix *Vtable = getVtablePrefix(VtablePtr); + if (!Vtable) + return DynamicTypeInfo(0, 0, 0); + const abi::__class_type_info *ObjectType = findBaseAtOffset( + static_cast<const abi::__class_type_info*>(Vtable->TypeInfo), + -Vtable->Offset); + return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, + ObjectType ? ObjectType->__type_name : "<unknown>"); +} + +#endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS diff --git a/libsanitizer/ubsan/ubsan_type_hash_win.cc b/libsanitizer/ubsan/ubsan_type_hash_win.cc new file mode 100644 index 00000000000..a2eb1a71cb9 --- /dev/null +++ b/libsanitizer/ubsan/ubsan_type_hash_win.cc @@ -0,0 +1,79 @@ +//===-- ubsan_type_hash_win.cc --------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of type hashing/lookup for Microsoft C++ ABI. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB && SANITIZER_WINDOWS +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +#include <typeinfo> + +struct CompleteObjectLocator { + int is_image_relative; + int offset_to_top; + int vfptr_offset; + int rtti_addr; + int chd_addr; + int obj_locator_addr; +}; + +struct CompleteObjectLocatorAbs { + int is_image_relative; + int offset_to_top; + int vfptr_offset; + std::type_info *rtti_addr; + void *chd_addr; + CompleteObjectLocator *obj_locator_addr; +}; + +bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { + // FIXME: Implement. + return false; +} + +__ubsan::DynamicTypeInfo +__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) { + // The virtual table may not have a complete object locator if the object + // was compiled without RTTI (i.e. we might be reading from some other global + // laid out before the virtual table), so we need to carefully validate each + // pointer dereference and perform sanity checks. + CompleteObjectLocator **obj_locator_ptr = + ((CompleteObjectLocator**)VtablePtr)-1; + if (!IsAccessibleMemoryRange((uptr)obj_locator_ptr, sizeof(void*))) + return DynamicTypeInfo(0, 0, 0); + + CompleteObjectLocator *obj_locator = *obj_locator_ptr; + if (!IsAccessibleMemoryRange((uptr)obj_locator, + sizeof(CompleteObjectLocator))) + return DynamicTypeInfo(0, 0, 0); + + std::type_info *tinfo; + if (obj_locator->is_image_relative == 1) { + char *image_base = ((char *)obj_locator) - obj_locator->obj_locator_addr; + tinfo = (std::type_info *)(image_base + obj_locator->rtti_addr); + } else if (obj_locator->is_image_relative == 0) + tinfo = ((CompleteObjectLocatorAbs *)obj_locator)->rtti_addr; + else + // Probably not a complete object locator. + return DynamicTypeInfo(0, 0, 0); + + if (!IsAccessibleMemoryRange((uptr)tinfo, sizeof(std::type_info))) + return DynamicTypeInfo(0, 0, 0); + + // Okay, this is probably a std::type_info. Request its name. + // FIXME: Implement a base class search like we do for Itanium. + return DynamicTypeInfo(tinfo->name(), obj_locator->offset_to_top, + "<unknown>"); +} + +#endif // CAN_SANITIZE_UB && SANITIZER_WINDOWS diff --git a/libsanitizer/ubsan/ubsan_value.cc b/libsanitizer/ubsan/ubsan_value.cc index e2f664d3b24..e327f6ffc2e 100644 --- a/libsanitizer/ubsan/ubsan_value.cc +++ b/libsanitizer/ubsan/ubsan_value.cc @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_value.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" @@ -79,7 +81,14 @@ FloatMax Value::getFloatValue() const { #endif case 32: { float Value; - internal_memcpy(&Value, &Val, 4); +#if defined(__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 + internal_memcpy(&Value, &Val, 4); +#endif return Value; } case 64: { @@ -98,3 +107,5 @@ FloatMax Value::getFloatValue() const { } UNREACHABLE("unexpected floating point bit width"); } + +#endif // CAN_SANITIZE_UB diff --git a/libsanitizer/ubsan/ubsan_value.h b/libsanitizer/ubsan/ubsan_value.h index abfd31fbd99..cca1ac85b77 100644 --- a/libsanitizer/ubsan/ubsan_value.h +++ b/libsanitizer/ubsan/ubsan_value.h @@ -12,12 +12,6 @@ #ifndef UBSAN_VALUE_H #define UBSAN_VALUE_H -// For now, only support Linux, FreeBSD and Darwin. Other platforms should -// be easy to add, and probably work as-is. -#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__APPLE__) -#error "UBSan not supported for this platform!" -#endif - #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" @@ -30,7 +24,6 @@ __extension__ typedef unsigned __int128 u128; #define HAVE_INT128_T 0 #endif - namespace __ubsan { /// \brief Largest integer types we support. |