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 }; Index: lib/sanitizer_common/sanitizer_suppressions.cc =================================================================== --- lib/sanitizer_common/sanitizer_suppressions.cc +++ lib/sanitizer_common/sanitizer_suppressions.cc @@ -20,8 +20,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) 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 @@ -15,6 +15,7 @@ #include "ubsan_value.h" #include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_suppressions.h" namespace __ubsan { @@ -212,6 +213,9 @@ MaybePrintStackTrace(pc, bp); \ } while (0) +void InitializeSuppressions(); +bool MatchSuppressions(const char *checkName, SuppressionType type); + } // 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,8 @@ #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 "sanitizer_common/sanitizer_placement_new.h" #include using namespace __ubsan; @@ -300,3 +302,38 @@ renderMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, NumRanges, Args); } + +static SuppressionContext *suppression_ctx; + +void __ubsan::InitializeSuppressions() { + CHECK(!suppression_ctx); + + if (!flags()->suppressions) + return; + + ALIGNED(64) static char placeholder[sizeof(SuppressionContext)]; + suppression_ctx = new(placeholder) SuppressionContext; + + char *suppressions_from_file; + uptr buffer_size; + if (ReadFileToBuffer(flags()->suppressions, &suppressions_from_file, + &buffer_size, 1 << 26 /* max_len */)) + suppression_ctx->Parse(suppressions_from_file); + if (flags()->suppressions[0] && !buffer_size) { + Printf("UBSan: failed to read suppressions file '%s'\n", + flags()->suppressions); + Die(); + } +} + +bool __ubsan::MatchSuppressions(const char *checkName, SuppressionType type) { + Suppression *s; + +#if !SANITIZER_CAN_USE_PREINIT_ARRAY + InitIfNecessary(); +#endif + + if (suppression_ctx && suppression_ctx->Match(checkName, type, &s)) + return true; + return false; +} Index: lib/ubsan/ubsan_flags.h =================================================================== --- lib/ubsan/ubsan_flags.h +++ lib/ubsan/ubsan_flags.h @@ -17,6 +17,7 @@ struct Flags { bool print_stacktrace; + const char* suppressions; }; extern Flags ubsan_flags; Index: lib/ubsan/ubsan_flags.cc =================================================================== --- lib/ubsan/ubsan_flags.cc +++ lib/ubsan/ubsan_flags.cc @@ -23,11 +23,14 @@ Flags *f = flags(); // Default values. f->print_stacktrace = false; + f->suppressions = ""; const char *options = GetEnv("UBSAN_OPTIONS"); if (options) { ParseFlag(options, &f->print_stacktrace, "print_stacktrace", "Include full stacktrace into an error report"); + ParseFlag(options, &f->suppressions, "suppressions", + "Suppress an error report as specified in the supplied file"); } } Index: lib/ubsan/ubsan_handlers_cxx.cc =================================================================== --- lib/ubsan/ubsan_handlers_cxx.cc +++ lib/ubsan/ubsan_handlers_cxx.cc @@ -33,6 +33,12 @@ // Just a cache miss. The type matches after all. return; + DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); + + // Suppress an error report if it is specified. + if (MatchSuppressions(DTI.getMostDerivedTypeName(), SuppressionVptrCheck)) + return; + SourceLocation Loc = Data->Loc.acquire(); if (Loc.isDisabled()) return; @@ -42,7 +48,6 @@ << 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()) Index: lib/ubsan/ubsan_init.cc =================================================================== --- lib/ubsan/ubsan_init.cc +++ lib/ubsan/ubsan_init.cc @@ -13,6 +13,7 @@ #include "ubsan_init.h" #include "ubsan_flags.h" +#include "ubsan_diag.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_flags.h" @@ -47,6 +48,7 @@ } // Initialize UBSan-specific flags. InitializeFlags(); + InitializeSuppressions(); 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,18 @@ // 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"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp +// RUN: (echo " " ; UBSAN_OPTIONS=suppressions=%t.supp %run %t mS 2>&1) | FileCheck %s --check-prefix=CHECK-SUPPRESS +// RUN: (echo " " ; UBSAN_OPTIONS=suppressions=%t.supp %run %t fS 2>&1) | FileCheck %s --check-prefix=CHECK-SUPPRESS +// RUN: (echo " " ; UBSAN_OPTIONS=suppressions=%t.supp %run %t cS 2>&1) | FileCheck %s --check-prefix=CHECK-SUPPRESS +// RUN: (echo " " ; UBSAN_OPTIONS=suppressions=%t.supp %run %t mV 2>&1) | FileCheck %s --check-prefix=CHECK-SUPPRESS +// RUN: (echo " " ; UBSAN_OPTIONS=suppressions=%t.supp %run %t fV 2>&1) | FileCheck %s --check-prefix=CHECK-SUPPRESS +// RUN: (echo " " ; UBSAN_OPTIONS=suppressions=%t.supp %run %t cV 2>&1) | FileCheck %s --check-prefix=CHECK-SUPPRESS +// RUN: (echo " " ; UBSAN_OPTIONS=suppressions=%t.supp %run %t oU 2>&1) | FileCheck %s --check-prefix=CHECK-SUPPRESS + +// RUN: echo "vptr_check:S" > %t.loc-supp +// RUN: UBSAN_OPTIONS=suppressions=%t.loc-supp %run %t s- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS + // FIXME: This test produces linker errors on Darwin. // XFAIL: darwin @@ -31,7 +43,11 @@ struct U : S, T { virtual int v() { return 2; } }; -T *p = 0; // Make p global so that lsan does not complain. +struct V : S {}; + +// Make p and p_tmp global so that lsan does not complain. +T *p = 0; +T *p_tmp = 0; int main(int, char **argv) { T t; @@ -119,5 +135,24 @@ // CHECK-DOWNCAST-NEXT: #0 {{.*}} in main {{.*}}vptr.cpp:[[@LINE+1]] static_cast(reinterpret_cast(p)); return 0; + + case 's': + 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(); + p_tmp = p; // Keep leak sanitizer silent. + // CHECK-LOC-SUPPRESS-NOT: [[PTR]]: note: object is of type 'S' + } + return 0; } } +// CHECK-SUPPRESS-NOT: runtime error: