diff --git a/compiler-rt/lib/rtsan/CMakeLists.txt b/compiler-rt/lib/rtsan/CMakeLists.txt index af34fb63cf53cc..f8dd4d735bc2a3 100644 --- a/compiler-rt/lib/rtsan/CMakeLists.txt +++ b/compiler-rt/lib/rtsan/CMakeLists.txt @@ -7,6 +7,7 @@ set(RTSAN_CXX_SOURCES rtsan_flags.cpp rtsan_interceptors.cpp rtsan_stats.cpp + rtsan_suppressions.cpp ) set(RTSAN_PREINIT_SOURCES @@ -14,12 +15,14 @@ set(RTSAN_PREINIT_SOURCES set(RTSAN_HEADERS rtsan.h + rtsan_checks.inc rtsan_assertions.h rtsan_context.h rtsan_diagnostics.h rtsan_flags.h rtsan_flags.inc rtsan_stats.h + rtsan_suppressions.h ) set(RTSAN_DEPS) diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index 7691815fd5c1dc..e9f42d3760aa82 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -14,12 +14,12 @@ #include "rtsan/rtsan_flags.h" #include "rtsan/rtsan_interceptors.h" #include "rtsan/rtsan_stats.h" +#include "rtsan/rtsan_suppressions.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" #include "sanitizer_common/sanitizer_stackdepot.h" -#include "sanitizer_common/sanitizer_stacktrace.h" using namespace __rtsan; using namespace __sanitizer; @@ -85,6 +85,8 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() { InitializeFlags(); InitializeInterceptors(); + InitializeSuppressions(); + if (flags().print_stats_on_exit) Atexit(PrintStatisticsSummary); diff --git a/compiler-rt/lib/rtsan/rtsan_assertions.h b/compiler-rt/lib/rtsan/rtsan_assertions.h index 745cbea0eb3a29..8183a8202478ff 100644 --- a/compiler-rt/lib/rtsan/rtsan_assertions.h +++ b/compiler-rt/lib/rtsan/rtsan_assertions.h @@ -15,6 +15,7 @@ #include "rtsan/rtsan.h" #include "rtsan/rtsan_context.h" #include "rtsan/rtsan_diagnostics.h" +#include "rtsan/rtsan_suppressions.h" #include "sanitizer_common/sanitizer_stacktrace.h" @@ -34,6 +35,9 @@ void ExpectNotRealtime(Context &context, const DiagnosticsInfo &info, stack.Unwind(info.pc, info.bp, nullptr, __sanitizer::common_flags()->fast_unwind_on_fatal); + if (IsStackTraceSuppressed(stack)) + return; + OnViolation(stack, info); } } diff --git a/compiler-rt/lib/rtsan/rtsan_checks.inc b/compiler-rt/lib/rtsan/rtsan_checks.inc new file mode 100644 index 00000000000000..f5f23e044bd5d7 --- /dev/null +++ b/compiler-rt/lib/rtsan/rtsan_checks.inc @@ -0,0 +1,19 @@ +//===-- rtsan_checks.inc ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// List of suppression checks handled by RTSan runtime. +// +//===----------------------------------------------------------------------===// +#ifndef RTSAN_CHECK +#error "Define RTSAN_CHECK prior to including this file!" +#endif + +// RTSAN_CHECK(Name, SummaryKind) +// SummaryKind should be a string literal. + +RTSAN_CHECK(CallStackContains, "call-stack-contains") diff --git a/compiler-rt/lib/rtsan/rtsan_flags.inc b/compiler-rt/lib/rtsan/rtsan_flags.inc index 1df71127d19d37..5c3eb3f53a5eb4 100644 --- a/compiler-rt/lib/rtsan/rtsan_flags.inc +++ b/compiler-rt/lib/rtsan/rtsan_flags.inc @@ -18,3 +18,4 @@ RTSAN_FLAG(bool, halt_on_error, true, "Exit after first reported error.") RTSAN_FLAG(bool, print_stats_on_exit, false, "Print stats on exit.") +RTSAN_FLAG(const char *, suppressions, "", "Suppressions file name.") diff --git a/compiler-rt/lib/rtsan/rtsan_suppressions.cpp b/compiler-rt/lib/rtsan/rtsan_suppressions.cpp new file mode 100644 index 00000000000000..c5051dd1910236 --- /dev/null +++ b/compiler-rt/lib/rtsan/rtsan_suppressions.cpp @@ -0,0 +1,94 @@ +//===--- rtsan_suppressions.cpp - Realtime Sanitizer ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the RTSan runtime, providing support for suppressions +// +//===----------------------------------------------------------------------===// + +#include "rtsan/rtsan_suppressions.h" + +#include "rtsan/rtsan_flags.h" + +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_suppressions.h" +#include "sanitizer_common/sanitizer_symbolizer.h" + +#include + +using namespace __sanitizer; +using namespace __rtsan; + +namespace { +enum class ErrorType { +#define RTSAN_CHECK(Name, FSanitizeFlagName) Name, +#include "rtsan_checks.inc" +#undef RTSAN_CHECK +}; +} // namespace + +alignas(64) static char suppression_placeholder[sizeof(SuppressionContext)]; +static SuppressionContext *suppression_ctx = nullptr; + +static const char *kSuppressionTypes[] = { +#define RTSAN_CHECK(Name, FSanitizeFlagName) FSanitizeFlagName, +#include "rtsan_checks.inc" +#undef RTSAN_CHECK +}; + +static const char *ConvertTypeToFlagName(ErrorType Type) { + switch (Type) { +#define RTSAN_CHECK(Name, FSanitizeFlagName) \ + case ErrorType::Name: \ + return FSanitizeFlagName; +#include "rtsan_checks.inc" +#undef RTSAN_CHECK + } + UNREACHABLE("unknown ErrorType!"); +} + +void __rtsan::InitializeSuppressions() { + CHECK_EQ(nullptr, suppression_ctx); + + // We will use suppression_ctx == nullptr as an early out + if (flags().suppressions[0] == '\0') + return; + + suppression_ctx = new (suppression_placeholder) + SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); + suppression_ctx->ParseFromFile(flags().suppressions); +} + +bool __rtsan::IsStackTraceSuppressed(const StackTrace &stack) { + if (suppression_ctx == nullptr) + return false; + + const char *call_stack_flag = + ConvertTypeToFlagName(ErrorType::CallStackContains); + if (!suppression_ctx->HasSuppressionType(call_stack_flag)) + return false; + + Symbolizer *symbolizer = Symbolizer::GetOrInit(); + for (uptr i = 0; i < stack.size && stack.trace[i]; i++) { + const uptr addr = stack.trace[i]; + + SymbolizedStackHolder symbolized_stack(symbolizer->SymbolizePC(addr)); + const SymbolizedStack *frames = symbolized_stack.get(); + CHECK(frames); + for (const SymbolizedStack *cur = frames; cur; cur = cur->next) { + const char *function_name = cur->info.function; + if (!function_name) + continue; + + Suppression *s; + if (suppression_ctx->Match(function_name, call_stack_flag, &s)) + return true; + } + } + return false; +} diff --git a/compiler-rt/lib/rtsan/rtsan_suppressions.h b/compiler-rt/lib/rtsan/rtsan_suppressions.h new file mode 100644 index 00000000000000..45545f8c0e0b65 --- /dev/null +++ b/compiler-rt/lib/rtsan/rtsan_suppressions.h @@ -0,0 +1,22 @@ +//===--- rtsan_suppressions.h - Realtime Sanitizer --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the RTSan runtime, providing support for suppressions +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "sanitizer_common/sanitizer_stacktrace.h" + +namespace __rtsan { + +void InitializeSuppressions(); +bool IsStackTraceSuppressed(const __sanitizer::StackTrace &stack); + +} // namespace __rtsan diff --git a/compiler-rt/test/rtsan/stack_suppressions.cpp b/compiler-rt/test/rtsan/stack_suppressions.cpp new file mode 100644 index 00000000000000..2aceedbb313b11 --- /dev/null +++ b/compiler-rt/test/rtsan/stack_suppressions.cpp @@ -0,0 +1,53 @@ +// RUN: %clangxx -fsanitize=realtime %s -o %t +// RUN: %env_rtsan_opts=suppressions='%s.supp' not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: ios + +// Intent: Ensure that suppressions work as intended + +#include +#include +#include + +#include + +void *MallocViolation() { return malloc(10); } + +void VectorViolations() { + // All of these should be suppressed by *vector* + std::vector v(10); + v.resize(20); + v.clear(); + v.resize(0); + v.push_back(1); + v.reserve(10); +} + +void BlockFunc() [[clang::blocking]] { usleep(1); } + +void *process() [[clang::nonblocking]] { + void *ptr = MallocViolation(); + VectorViolations(); + BlockFunc(); + free(ptr); + + // This is the one that should abort the program + // Everything else is suppressed + usleep(1); + + return ptr; +} + +int main() { + process(); + return 0; +} + +// CHECK-NOT: failed to open suppressions file +// CHECK: Intercepted call to real-time unsafe function +// CHECK-SAME: usleep + +// CHECK-NOT: Intercepted call to real-time unsafe function +// CHECK-NOT: malloc +// CHECK-NOT: vector +// CHECK-NOT: free +// CHECK-NOT: BlockFunc diff --git a/compiler-rt/test/rtsan/stack_suppressions.cpp.supp b/compiler-rt/test/rtsan/stack_suppressions.cpp.supp new file mode 100644 index 00000000000000..bec4db259a3e0e --- /dev/null +++ b/compiler-rt/test/rtsan/stack_suppressions.cpp.supp @@ -0,0 +1,4 @@ +call-stack-contains:MallocViolation +call-stack-contains:std::*vector +call-stack-contains:free +call-stack-contains:BlockFunc