[compiler-rt] r256018 - [UBSan] Implement runtime suppressions (PR25066).

Alexey Samsonov via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 18 11:56:42 PST 2015


Author: samsonov
Date: Fri Dec 18 13:56:42 2015
New Revision: 256018

URL: http://llvm.org/viewvc/llvm-project?rev=256018&view=rev
Log:
[UBSan] Implement runtime suppressions (PR25066).

Summary:
Add the ability to suppress UBSan reports for files/functions/modules
at runtime. The user can now pass UBSAN_OPTIONS=suppressions=supp.txt
with the contents of the form:

signed-integer-overflow:file-with-known-overflow.cpp
alignment:function_doing_unaligned_access
vptr:shared_object_with_vptr_failures.so

Suppression categories match the arguments passed to -fsanitize=
flag (although, see below). There is no overhead if suppressions are
not provided. Otherwise there is extra overhead for symbolization.

Limitations:
1) sometimes suppressions need debug info / symbol table to function
   properly (although sometimes frontend generates enough info to
   do the match).
2) it's only possible to suppress recoverable UB kinds - if you've
   built the code with -fno-sanitize-recover=undefined, suppressions
   will not work.
3) categories are fine-grained check kinds, not groups like "undefined"
   or "integer", so you can't write "undefined:file_with_ub.cc".

Reviewers: rsmith, kcc

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D15363

Added:
    compiler-rt/trunk/test/ubsan/TestCases/Integer/suppressions.cpp
Modified:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h
    compiler-rt/trunk/lib/ubsan/ubsan_diag.cc
    compiler-rt/trunk/lib/ubsan/ubsan_diag.h
    compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc
    compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc

Modified: compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h?rev=256018&r1=256017&r2=256018&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h (original)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_suppressions.h Fri Dec 18 13:56:42 2015
@@ -43,7 +43,7 @@ class SuppressionContext {
   void GetMatched(InternalMmapVector<Suppression *> *matched);
 
  private:
-  static const int kMaxSuppressionTypes = 16;
+  static const int kMaxSuppressionTypes = 32;
   const char **const suppression_types_;
   const int suppression_types_num_;
 

Modified: compiler-rt/trunk/lib/ubsan/ubsan_diag.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_diag.cc?rev=256018&r1=256017&r2=256018&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_diag.cc (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_diag.cc Fri Dec 18 13:56:42 2015
@@ -54,6 +54,17 @@ static const char *ConvertTypeToString(E
   UNREACHABLE("unknown ErrorType!");
 }
 
+static const char *ConvertTypeToFlagName(ErrorType Type) {
+  switch (Type) {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName)                      \
+  case ErrorType::Name:                                                        \
+    return FSanitizeFlagName;
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+  }
+  UNREACHABLE("unknown ErrorType!");
+}
+
 static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
   if (!common_flags()->print_summary)
     return;
@@ -372,7 +383,12 @@ ScopedReport::~ScopedReport() {
 ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
 static SuppressionContext *suppression_ctx = nullptr;
 static const char kVptrCheck[] = "vptr_check";
-static const char *kSuppressionTypes[] = { kVptrCheck };
+static const char *kSuppressionTypes[] = {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+    kVptrCheck,
+};
 
 void __ubsan::InitializeSuppressions() {
   CHECK_EQ(nullptr, suppression_ctx);
@@ -388,4 +404,28 @@ bool __ubsan::IsVptrCheckSuppressed(cons
   return suppression_ctx->Match(TypeName, kVptrCheck, &s);
 }
 
+bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
+  InitAsStandaloneIfNecessary();
+  CHECK(suppression_ctx);
+  const char *SuppType = ConvertTypeToFlagName(ET);
+  // Fast path: don't symbolize PC if there is no suppressions for given UB
+  // type.
+  if (!suppression_ctx->HasSuppressionType(SuppType))
+    return false;
+  Suppression *s = nullptr;
+  // Suppress by file name known to runtime.
+  if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
+    return true;
+  // Suppress by module name.
+  if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
+    if (suppression_ctx->Match(Module, SuppType, &s))
+      return true;
+  }
+  // Suppress by function or source file name from debug info.
+  SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
+  const AddressInfo &AI = Stack.get()->info;
+  return suppression_ctx->Match(AI.function, SuppType, &s) ||
+         suppression_ctx->Match(AI.file, SuppType, &s);
+}
+
 #endif  // CAN_SANITIZE_UB

Modified: compiler-rt/trunk/lib/ubsan/ubsan_diag.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_diag.h?rev=256018&r1=256017&r2=256018&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_diag.h (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_diag.h Fri Dec 18 13:56:42 2015
@@ -225,7 +225,7 @@ enum class ErrorType {
 #undef UBSAN_CHECK
 };
 
-bool ignoreReport(SourceLocation SLoc, ReportOptions Opts);
+bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET);
 
 #define GET_REPORT_OPTIONS(unrecoverable_handler) \
     GET_CALLER_PC_BP; \
@@ -246,6 +246,9 @@ public:
 
 void InitializeSuppressions();
 bool IsVptrCheckSuppressed(const char *TypeName);
+// Sometimes UBSan runtime can know filename from handlers arguments, even if
+// debug info is missing.
+bool IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename);
 
 } // namespace __ubsan
 

Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc?rev=256018&r1=256017&r2=256018&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers.cc Fri Dec 18 13:56:42 2015
@@ -22,7 +22,7 @@ using namespace __sanitizer;
 using namespace __ubsan;
 
 namespace __ubsan {
-bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
+bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) {
   // We are not allowed to skip error report: if we are in unrecoverable
   // handler, we have to terminate the program right now, and therefore
   // have to print some diagnostic.
@@ -32,7 +32,7 @@ bool ignoreReport(SourceLocation SLoc, R
   // thread could have acquired it, but not yet printed the report.
   if (Opts.FromUnrecoverableHandler)
     return false;
-  return SLoc.isDisabled();
+  return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename());
 }
 
 const char *TypeCheckKinds[] = {
@@ -44,15 +44,6 @@ const char *TypeCheckKinds[] = {
 static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
                                    ReportOptions Opts) {
   Location Loc = Data->Loc.acquire();
-  // Use the SourceLocation from Data to track deduplication, even if 'invalid'
-  if (ignoreReport(Loc.getSourceLocation(), Opts))
-    return;
-
-  SymbolizedStackHolder FallbackLoc;
-  if (Data->Loc.isInvalid()) {
-    FallbackLoc.reset(getCallerLocation(Opts.pc));
-    Loc = FallbackLoc;
-  }
 
   ErrorType ET;
   if (!Pointer)
@@ -62,6 +53,17 @@ static void handleTypeMismatchImpl(TypeM
   else
     ET = ErrorType::InsufficientObjectSize;
 
+  // Use the SourceLocation from Data to track deduplication, even if it's
+  // invalid.
+  if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
+    return;
+
+  SymbolizedStackHolder FallbackLoc;
+  if (Data->Loc.isInvalid()) {
+    FallbackLoc.reset(getCallerLocation(Opts.pc));
+    Loc = FallbackLoc;
+  }
+
   ScopedReport R(Opts, Loc, ET);
 
   switch (ET) {
@@ -106,12 +108,14 @@ static void handleIntegerOverflowImpl(Ov
                                       const char *Operator, T RHS,
                                       ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  bool IsSigned = Data->Type.isSignedIntegerTy();
+  ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+                          : ErrorType::UnsignedIntegerOverflow;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  bool IsSigned = Data->Type.isSignedIntegerTy();
-  ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
-                                     : ErrorType::UnsignedIntegerOverflow);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "%0 integer overflow: "
                       "%1 %2 %3 cannot be represented in type %4")
@@ -138,12 +142,14 @@ UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mu
 static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
                                      ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  bool IsSigned = Data->Type.isSignedIntegerTy();
+  ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+                          : ErrorType::UnsignedIntegerOverflow;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  bool IsSigned = Data->Type.isSignedIntegerTy();
-  ScopedReport R(Opts, Loc, IsSigned ? ErrorType::SignedIntegerOverflow
-                                     : ErrorType::UnsignedIntegerOverflow);
+  ScopedReport R(Opts, Loc, ET);
 
   if (IsSigned)
     Diag(Loc, DL_Error,
@@ -170,9 +176,6 @@ void __ubsan::__ubsan_handle_negate_over
 static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
                                      ValueHandle RHS, ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
-    return;
-
   Value LHSVal(Data->Type, LHS);
   Value RHSVal(Data->Type, RHS);
 
@@ -184,6 +187,9 @@ static void handleDivremOverflowImpl(Ove
   else
     ET = ErrorType::FloatDivideByZero;
 
+  if (ignoreReport(Loc, Opts, ET))
+    return;
+
   ScopedReport R(Opts, Loc, ET);
 
   switch (ET) {
@@ -214,9 +220,6 @@ static void handleShiftOutOfBoundsImpl(S
                                        ValueHandle LHS, ValueHandle RHS,
                                        ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
-    return;
-
   Value LHSVal(Data->LHSType, LHS);
   Value RHSVal(Data->RHSType, RHS);
 
@@ -227,6 +230,9 @@ static void handleShiftOutOfBoundsImpl(S
   else
     ET = ErrorType::InvalidShiftBase;
 
+  if (ignoreReport(Loc, Opts, ET))
+    return;
+
   ScopedReport R(Opts, Loc, ET);
 
   if (ET == ErrorType::InvalidShiftExponent) {
@@ -263,10 +269,12 @@ void __ubsan::__ubsan_handle_shift_out_o
 static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
                                   ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::OutOfBoundsIndex;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::OutOfBoundsIndex);
+  ScopedReport R(Opts, Loc, ET);
 
   Value IndexVal(Data->IndexType, Index);
   Diag(Loc, DL_Error, "index %0 out of bounds for type %1")
@@ -313,10 +321,12 @@ void __ubsan::__ubsan_handle_missing_ret
 static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
                                       ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::NonPositiveVLAIndex;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::NonPositiveVLAIndex);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "variable length array bound evaluates to "
                       "non-positive value %0")
@@ -358,6 +368,7 @@ static void handleFloatCastOverflow(void
   SymbolizedStackHolder CallerLoc;
   Location Loc;
   const TypeDescriptor *FromType, *ToType;
+  ErrorType ET = ErrorType::FloatCastOverflow;
 
   if (looksLikeFloatCastOverflowDataV1(DataPtr)) {
     auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr);
@@ -368,14 +379,14 @@ static void handleFloatCastOverflow(void
   } else {
     auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr);
     SourceLocation SLoc = Data->Loc.acquire();
-    if (ignoreReport(SLoc, Opts))
+    if (ignoreReport(SLoc, Opts, ET))
       return;
     Loc = SLoc;
     FromType = &Data->FromType;
     ToType = &Data->ToType;
   }
 
-  ScopedReport R(Opts, Loc, ErrorType::FloatCastOverflow);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error,
        "value %0 is outside the range of representable values of type %2")
@@ -396,14 +407,16 @@ void __ubsan::__ubsan_handle_float_cast_
 static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
                                    ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
-    return;
-
   // This check could be more precise if we used different handlers for
   // -fsanitize=bool and -fsanitize=enum.
   bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'"));
-  ScopedReport R(Opts, Loc, IsBool ? ErrorType::InvalidBoolLoad
-                                   : ErrorType::InvalidEnumLoad);
+  ErrorType ET =
+      IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad;
+
+  if (ignoreReport(Loc, Opts, ET))
+    return;
+
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error,
        "load of value %0, which is not a valid value for type %1")
@@ -426,10 +439,12 @@ static void handleFunctionTypeMismatch(F
                                        ValueHandle Function,
                                        ReportOptions Opts) {
   SourceLocation CallLoc = Data->Loc.acquire();
-  if (ignoreReport(CallLoc, Opts))
+  ErrorType ET = ErrorType::FunctionTypeMismatch;
+
+  if (ignoreReport(CallLoc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, CallLoc, ErrorType::FunctionTypeMismatch);
+  ScopedReport R(Opts, CallLoc, ET);
 
   SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
   const char *FName = FLoc.get()->info.function;
@@ -458,10 +473,12 @@ void __ubsan::__ubsan_handle_function_ty
 
 static void handleNonNullReturn(NonNullReturnData *Data, ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::InvalidNullReturn;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::InvalidNullReturn);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "null pointer returned from function declared to never "
                       "return null");
@@ -482,10 +499,12 @@ void __ubsan::__ubsan_handle_nonnull_ret
 
 static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::InvalidNullArgument;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::InvalidNullArgument);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to "
        "never be null") << Data->ArgIndex;
@@ -507,10 +526,12 @@ void __ubsan::__ubsan_handle_nonnull_arg
 static void handleCFIBadIcall(CFIBadIcallData *Data, ValueHandle Function,
                               ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
-  if (ignoreReport(Loc, Opts))
+  ErrorType ET = ErrorType::CFIBadType;
+
+  if (ignoreReport(Loc, Opts, ET))
     return;
 
-  ScopedReport R(Opts, Loc, ErrorType::CFIBadType);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
                       "indirect function call")

Modified: compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc?rev=256018&r1=256017&r2=256018&view=diff
==============================================================================
--- compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc (original)
+++ compiler-rt/trunk/lib/ubsan/ubsan_handlers_cxx.cc Fri Dec 18 13:56:42 2015
@@ -43,10 +43,11 @@ static bool HandleDynamicTypeCacheMiss(
     return false;
 
   SourceLocation Loc = Data->Loc.acquire();
-  if (Loc.isDisabled())
+  ErrorType ET = ErrorType::DynamicTypeMismatch;
+  if (ignoreReport(Loc, Opts, ET))
     return false;
 
-  ScopedReport R(Opts, Loc, ErrorType::DynamicTypeMismatch);
+  ScopedReport R(Opts, Loc, ET);
 
   Diag(Loc, DL_Error,
        "%0 address %1 which does not point to an object of type %2")
@@ -89,10 +90,12 @@ void __ubsan::__ubsan_handle_dynamic_typ
 static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable,
                              ReportOptions Opts) {
   SourceLocation Loc = Data->Loc.acquire();
+  ErrorType ET = ErrorType::CFIBadType;
 
-  if (ignoreReport(Loc, Opts))
+  if (ignoreReport(Loc, Opts, ET))
     return;
-  ScopedReport R(Opts, Loc, ErrorType::CFIBadType);
+
+  ScopedReport R(Opts, Loc, ET);
   DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable);
 
   static const char *TypeCheckKinds[] = {

Added: compiler-rt/trunk/test/ubsan/TestCases/Integer/suppressions.cpp
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/ubsan/TestCases/Integer/suppressions.cpp?rev=256018&view=auto
==============================================================================
--- compiler-rt/trunk/test/ubsan/TestCases/Integer/suppressions.cpp (added)
+++ compiler-rt/trunk/test/ubsan/TestCases/Integer/suppressions.cpp Fri Dec 18 13:56:42 2015
@@ -0,0 +1,33 @@
+// RUN: %clangxx -fsanitize=integer -g0 %s -o %t
+
+// Fails without any suppression.
+// RUN: %env_ubsan_opts=halt_on_error=1 not %run %t 2>&1 | FileCheck %s
+
+// RUN: echo "signed-integer-overflow:%t" > %t.wrong-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions="%t.wrong-supp" not %run %t 2>&1 | FileCheck %s
+
+// RUN: echo "unsigned-integer-overflow:do_overflow" > %t.func-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions="%t.func-supp" %run %t
+// RUN: echo "unsigned-integer-overflow:%t" > %t.module-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions="%t.module-supp" %run %t
+
+// Note: file-level suppressions should work even without debug info.
+// RUN: echo "unsigned-integer-overflow:%s" > %t.file-supp
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions="%t.file-supp" %run %t
+
+// Suppressions don't work for unrecoverable kinds.
+// RUN: %clangxx -fsanitize=integer -fno-sanitize-recover=integer %s -o %t-norecover
+// RUN: %env_ubsan_opts=halt_on_error=1:suppressions="%t.module-supp" not %run %t-norecover 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+extern "C" void do_overflow() {
+  (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull));
+  // CHECK: runtime error: unsigned integer overflow
+}
+
+int main() {
+  do_overflow();
+  return 0;
+}
+




More information about the llvm-commits mailing list