Index: lib/sanitizer_common/sanitizer_suppressions.h =================================================================== --- lib/sanitizer_common/sanitizer_suppressions.h +++ lib/sanitizer_common/sanitizer_suppressions.h @@ -27,6 +27,7 @@ SuppressionLeak, SuppressionLib, SuppressionDeadlock, + SuppressionVptrCheck, SuppressionTypeCount }; @@ -48,6 +49,10 @@ // Create a SuppressionContext singleton. Not thread safe. Must be called // early during initialization. static void Init(); + + // Initialize a SuppressionContext singleton only if necessary. + static void InitIfNecessary(); + // Returns a SuppressionContext singleton. static SuppressionContext *Get(); Index: lib/sanitizer_common/sanitizer_suppressions.cc =================================================================== --- lib/sanitizer_common/sanitizer_suppressions.cc +++ lib/sanitizer_common/sanitizer_suppressions.cc @@ -22,8 +22,9 @@ namespace __sanitizer { static const char *const kTypeStrings[SuppressionTypeCount] = { - "none", "race", "mutex", "thread", - "signal", "leak", "called_from_lib", "deadlock"}; + "none", "race", "mutex", "thread", + "signal", "leak", "called_from_lib", "deadlock", + "vptr_check"}; bool TemplateMatch(char *templ, const char *str) { if (str == 0 || str[0] == 0) @@ -75,6 +76,11 @@ return suppression_ctx; } +void SuppressionContext::InitIfNecessary() { + if (!suppression_ctx) + Init(); +} + void SuppressionContext::Init() { CHECK(!suppression_ctx); suppression_ctx = new(placeholder) SuppressionContext; Index: lib/sanitizer_common/tests/sanitizer_suppressions_test.cc =================================================================== --- lib/sanitizer_common/tests/sanitizer_suppressions_test.cc +++ lib/sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -66,11 +66,13 @@ CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib), - "called_from_lib")); - CHECK( - !internal_strcmp(SuppressionTypeString(SuppressionDeadlock), "deadlock")); + "called_from_lib")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionDeadlock), + "deadlock")); + CHECK(!internal_strcmp(SuppressionTypeString(SuppressionVptrCheck), + "vptr_check")); // Ensure this test is up-to-date when suppression types are added. - CHECK_EQ(SuppressionTypeCount, 8); + CHECK_EQ(SuppressionTypeCount, 9); } class SuppressionContextTest : public ::testing::Test { Index: lib/ubsan/ubsan_diag.h =================================================================== --- lib/ubsan/ubsan_diag.h +++ lib/ubsan/ubsan_diag.h @@ -218,6 +218,8 @@ ~ScopedReport(); }; +bool TypeIsSuppressed(const char *TypeName); + } // namespace __ubsan #endif // UBSAN_DIAG_H Index: lib/ubsan/ubsan_diag.cc =================================================================== --- lib/ubsan/ubsan_diag.cc +++ lib/ubsan/ubsan_diag.cc @@ -17,6 +17,7 @@ #include "sanitizer_common/sanitizer_report_decorator.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include "sanitizer_common/sanitizer_suppressions.h" #include using namespace __ubsan; @@ -312,3 +313,18 @@ if (DieAfterReport) Die(); } + +static bool MatchSuppressions(const char *Str, SuppressionType Type) { + 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); +} + +bool __ubsan::TypeIsSuppressed(const char *TypeName) { + return MatchSuppressions(TypeName, SuppressionVptrCheck); +} Index: lib/ubsan/ubsan_handlers_cxx.cc =================================================================== --- lib/ubsan/ubsan_handlers_cxx.cc +++ lib/ubsan/ubsan_handlers_cxx.cc @@ -33,6 +33,13 @@ // Just a cache miss. The type matches after all. return; + DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); + MangledName MDTMangledName = MangledName(DTI.getMostDerivedTypeName()); + + // Check if an error report is suppressed. + if (TypeIsSuppressed(MDTMangledName.getName())) + return; + SourceLocation Loc = Data->Loc.acquire(); if (Loc.isDisabled()) return; @@ -44,21 +51,20 @@ << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type; // If possible, say what type it actually points to. - DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); if (!DTI.isValid()) Diag(Pointer, DL_Note, "object has invalid vptr") - << MangledName(DTI.getMostDerivedTypeName()) + << MDTMangledName << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); else if (!DTI.getOffset()) Diag(Pointer, DL_Note, "object is of type %0") - << MangledName(DTI.getMostDerivedTypeName()) + << MDTMangledName << 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()) + << DTI.getOffset() << MDTMangledName << MangledName(DTI.getSubobjectTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1"); Index: lib/ubsan/ubsan_init.cc =================================================================== --- lib/ubsan/ubsan_init.cc +++ lib/ubsan/ubsan_init.cc @@ -17,6 +17,7 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include "sanitizer_common/sanitizer_suppressions.h" using namespace __ubsan; @@ -42,6 +43,7 @@ } // Initialize UBSan-specific flags. InitializeFlags(); + SuppressionContext::InitIfNecessary(); ubsan_inited = true; } Index: test/ubsan/TestCases/TypeCheck/vptr.cpp =================================================================== --- test/ubsan/TestCases/TypeCheck/vptr.cpp +++ test/ubsan/TestCases/TypeCheck/vptr.cpp @@ -11,6 +11,19 @@ // RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace // RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace +// RUN: echo "vptr_check:S" > %t.loc-supp +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.loc-supp" UBSAN_OPTIONS=suppressions=%t.loc-supp %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS + +// RUN: %clangxx -fsanitize=vptr -fno-sanitize-recover -g %s -O3 -o %t +// RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.supp" UBSAN_OPTIONS=suppressions=%t.supp %run %t mS 2>&1 +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.supp" UBSAN_OPTIONS=suppressions=%t.supp %run %t fS 2>&1 +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.supp" UBSAN_OPTIONS=suppressions=%t.supp %run %t cS 2>&1 +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.supp" UBSAN_OPTIONS=suppressions=%t.supp %run %t mV 2>&1 +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.supp" UBSAN_OPTIONS=suppressions=%t.supp %run %t fV 2>&1 +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.supp" UBSAN_OPTIONS=suppressions=%t.supp %run %t cV 2>&1 +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.supp" UBSAN_OPTIONS=suppressions=%t.supp %run %t oU 2>&1 + // FIXME: This test produces linker errors on Darwin. // XFAIL: darwin @@ -31,6 +44,8 @@ struct U : S, T { virtual int v() { return 2; } }; +struct V : S {}; + // Make p global so that lsan does not complain. T *p = 0; @@ -84,6 +99,22 @@ {T &r = *p;} break; + case 'x': + for (int i=0; i<2; i++) { + if (i==0) + p = reinterpret_cast(new S); + else + p = reinterpret_cast(new V); + // CHECK-LOC-SUPPRESS: vptr.cpp:[[@LINE+5]]:7: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-LOC-SUPPRESS-NEXT: [[PTR]]: note: object is of type 'V' + // CHECK-LOC-SUPPRESS-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} + // CHECK-LOC-SUPPRESS-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} + // CHECK-LOC-SUPPRESS-NEXT: {{^ vptr for 'V'}} + p->g(); + // CHECK-LOC-SUPPRESS-NOT: [[PTR]]: note: object is of type 'S' + } + return 0; + case 'm': // CHECK-MEMBER: vptr.cpp:[[@LINE+6]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]