From ff24fd25163c49c2bed88f121c6cd73aae29cfa5 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov Date: Tue, 5 Aug 2014 01:24:22 +0000 Subject: [PATCH] [UBSan] Allow to suppress reports from vptr checker for specified types. Based on http://reviews.llvm.org/D4702 by Byoungyoung Lee! llvm-svn: 214833 --- .../sanitizer_suppressions.cc | 4 +-- .../sanitizer_common/sanitizer_suppressions.h | 1 + .../tests/sanitizer_suppressions_test.cc | 6 ++-- compiler-rt/lib/ubsan/ubsan_diag.cc | 9 ++++++ compiler-rt/lib/ubsan/ubsan_diag.h | 3 ++ compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc | 23 +++++++++------ compiler-rt/lib/ubsan/ubsan_init.cc | 2 ++ .../test/ubsan/TestCases/TypeCheck/vptr.cpp | 28 +++++++++++++++++++ 8 files changed, 64 insertions(+), 12 deletions(-) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc b/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc index 1716dcc44cb1..7f76693e57cb 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.cc @@ -22,8 +22,8 @@ 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) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h b/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h index 33f7250397a9..37fd3c4f0a8e 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_suppressions.h @@ -27,6 +27,7 @@ enum SuppressionType { SuppressionLeak, SuppressionLib, SuppressionDeadlock, + SuppressionVptrCheck, SuppressionTypeCount }; diff --git a/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc index 93fc8a35dc80..2a1e356b9bda 100644 --- a/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc +++ b/compiler-rt/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc @@ -66,11 +66,13 @@ TEST(Suppressions, TypeStrings) { CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak")); CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib), - "called_from_lib")); + "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(9, SuppressionTypeCount); } class SuppressionContextTest : public ::testing::Test { diff --git a/compiler-rt/lib/ubsan/ubsan_diag.cc b/compiler-rt/lib/ubsan/ubsan_diag.cc index 2b2ca29fac7e..2fca8d1c2ae0 100644 --- a/compiler-rt/lib/ubsan/ubsan_diag.cc +++ b/compiler-rt/lib/ubsan/ubsan_diag.cc @@ -312,3 +312,12 @@ ScopedReport::~ScopedReport() { if (DieAfterReport) Die(); } + +bool __ubsan::MatchSuppression(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); +} diff --git a/compiler-rt/lib/ubsan/ubsan_diag.h b/compiler-rt/lib/ubsan/ubsan_diag.h index 4c8a3ea137b3..77aaa6764b30 100644 --- a/compiler-rt/lib/ubsan/ubsan_diag.h +++ b/compiler-rt/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 { @@ -218,6 +219,8 @@ public: ~ScopedReport(); }; +bool MatchSuppression(const char *Str, SuppressionType Type); + } // namespace __ubsan #endif // UBSAN_DIAG_H diff --git a/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc b/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc index 205362e7b812..4002e61ef92f 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc +++ b/compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc @@ -18,6 +18,7 @@ #include "ubsan_type_hash.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_suppressions.h" using namespace __sanitizer; using namespace __ubsan; @@ -33,6 +34,12 @@ static void HandleDynamicTypeCacheMiss( // Just a cache miss. The type matches after all. return; + // Check if error report should be suppressed. + DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); + if (DTI.isValid() && + MatchSuppression(DTI.getMostDerivedTypeName(), SuppressionVptrCheck)) + return; + SourceLocation Loc = Data->Loc.acquire(); if (Loc.isDisabled()) return; @@ -44,23 +51,23 @@ static void HandleDynamicTypeCacheMiss( << 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()) - << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); + << MangledName(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()) - << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0"); + << MangledName(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()) - << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1"); + << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName()) + << MangledName(DTI.getSubobjectTypeName()) + << Range(Pointer, Pointer + sizeof(uptr), + "vptr for %2 base class of %1"); MaybePrintStackTrace(pc, bp); } diff --git a/compiler-rt/lib/ubsan/ubsan_init.cc b/compiler-rt/lib/ubsan/ubsan_init.cc index 01fd17ab0d2c..8751ec11257d 100644 --- a/compiler-rt/lib/ubsan/ubsan_init.cc +++ b/compiler-rt/lib/ubsan/ubsan_init.cc @@ -16,6 +16,7 @@ #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; @@ -42,6 +43,7 @@ void __ubsan::InitIfNecessary() { } // Initialize UBSan-specific flags. InitializeFlags(); + SuppressionContext::InitIfNecessary(); ubsan_inited = true; } diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/vptr.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/vptr.cpp index 7fd5804da802..3720cb21fd1e 100644 --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/vptr.cpp +++ b/compiler-rt/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: %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 + +// RUN: echo "vptr_check:S" > %t.loc-supp +// RUN: ASAN_OPTIONS="$ASAN_OPTIONS suppressions=%t.loc-supp" UBSAN_OPTIONS=suppressions=%t.loc-supp not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS + // FIXME: This test produces linker errors on Darwin. // XFAIL: darwin @@ -31,6 +44,8 @@ struct T : S { 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,19 @@ int access_p(T *p, char type) { {T &r = *p;} break; + case 'x': + for (int i = 0; i < 2; i++) { + // Check that the first iteration ("S") succeeds, while the second ("V") fails. + p = reinterpret_cast((i == 0) ? new S : 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(); + } + 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']]