diff --git a/compiler-rt/lib/rtsan/CMakeLists.txt b/compiler-rt/lib/rtsan/CMakeLists.txt index 0fc3a3f8f4896..dec40858dbfa4 100644 --- a/compiler-rt/lib/rtsan/CMakeLists.txt +++ b/compiler-rt/lib/rtsan/CMakeLists.txt @@ -5,7 +5,9 @@ set(RTSAN_CXX_SOURCES rtsan_context.cpp rtsan_diagnostics.cpp rtsan_flags.cpp - rtsan_interceptors.cpp) + rtsan_interceptors.cpp + rtsan_stats.cpp + ) set(RTSAN_PREINIT_SOURCES rtsan_preinit.cpp) @@ -16,7 +18,9 @@ set(RTSAN_HEADERS rtsan_context.h rtsan_diagnostics.h rtsan_flags.h - rtsan_flags.inc) + rtsan_flags.inc + rtsan_stats.h + ) set(RTSAN_DEPS) diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index b288da64ffbe2..f688b06886720 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" @@ -22,30 +23,56 @@ using namespace __rtsan; using namespace __sanitizer; +namespace { +enum class InitializationState : u8 { + Uninitialized, + Initializing, + Initialized, +}; +} // namespace + static StaticSpinMutex rtsan_inited_mutex; static atomic_uint8_t rtsan_initialized = {0}; -static void SetInitialized() { - atomic_store(&rtsan_initialized, 1, memory_order_release); +static void SetInitializationState(InitializationState state) { + atomic_store(&rtsan_initialized, static_cast(state), + memory_order_release); +} + +static InitializationState GetInitializationState() { + return static_cast( + atomic_load(&rtsan_initialized, memory_order_acquire)); } -static auto PrintDiagnosticsAndDieAction(DiagnosticsInfo info) { - return [info]() { - __rtsan::PrintDiagnostics(info); - Die(); +static void RtsanAtexit() { PrintStatisticsSummary(); } + +static auto DefaultErrorAction(DiagnosticsInfo info) { + return [info](const BufferedStackTrace &stack) { + if (flags().print_stats_on_exit) + IncrementTotalErrorCount(); + + PrintDiagnostics(info); + stack.Print(); + + if (flags().halt_on_error) + Die(); }; } extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() { - CHECK(!__rtsan_is_initialized()); + CHECK(GetInitializationState() == InitializationState::Uninitialized); + SetInitializationState(InitializationState::Initializing); SanitizerToolName = "RealtimeSanitizer"; InitializeFlags(); InitializeInterceptors(); - SetInitialized(); + if (flags().print_stats_on_exit) + Atexit(RtsanAtexit); + + SetInitializationState(InitializationState::Initialized); } SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_ensure_initialized() { @@ -62,7 +89,7 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_ensure_initialized() { } SANITIZER_INTERFACE_ATTRIBUTE bool __rtsan_is_initialized() { - return atomic_load(&rtsan_initialized, memory_order_acquire) == 1; + return GetInitializationState() == InitializationState::Initialized; } SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() { @@ -83,12 +110,16 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable() { SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_notify_intercepted_call(const char *func_name) { + // While initializing, we need all intercepted functions to behave normally + if (GetInitializationState() == InitializationState::Initializing) + return; + __rtsan_ensure_initialized(); GET_CALLER_PC_BP; ExpectNotRealtime( GetContextForThisThread(), - PrintDiagnosticsAndDieAction( - {DiagnosticsInfoType::InterceptedCall, func_name, pc, bp})); + DefaultErrorAction({DiagnosticsInfoType::InterceptedCall, func_name}), pc, + bp); } SANITIZER_INTERFACE_ATTRIBUTE void @@ -97,8 +128,8 @@ __rtsan_notify_blocking_call(const char *func_name) { GET_CALLER_PC_BP; ExpectNotRealtime( GetContextForThisThread(), - PrintDiagnosticsAndDieAction( - {DiagnosticsInfoType::BlockingCall, func_name, pc, bp})); + DefaultErrorAction({DiagnosticsInfoType::BlockingCall, func_name}), pc, + bp); } } // extern "C" diff --git a/compiler-rt/lib/rtsan/rtsan_assertions.h b/compiler-rt/lib/rtsan/rtsan_assertions.h index 1a653d198ab88..bded711d88a92 100644 --- a/compiler-rt/lib/rtsan/rtsan_assertions.h +++ b/compiler-rt/lib/rtsan/rtsan_assertions.h @@ -14,15 +14,25 @@ #include "rtsan/rtsan.h" #include "rtsan/rtsan_context.h" +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_stacktrace.h" namespace __rtsan { template -void ExpectNotRealtime(Context &context, OnViolationAction &&OnViolation) { +void ExpectNotRealtime(Context &context, OnViolationAction &&OnViolation, + __sanitizer::uptr caller_pc, + __sanitizer::uptr caller_bp) { CHECK(__rtsan_is_initialized()); if (context.InRealtimeContext() && !context.IsBypassed()) { context.BypassPush(); - OnViolation(); + + __sanitizer::BufferedStackTrace stack; + stack.Unwind(caller_pc, caller_bp, nullptr, + /*fast*/ true); + + OnViolation(stack); + context.BypassPop(); } } diff --git a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp index f82001f5b2057..cfe71481d3dc7 100644 --- a/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp +++ b/compiler-rt/lib/rtsan/rtsan_diagnostics.cpp @@ -39,13 +39,6 @@ class Decorator : public __sanitizer::SanitizerCommonDecorator { }; } // namespace -static void PrintStackTrace(uptr pc, uptr bp) { - BufferedStackTrace stack{}; - - stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal); - stack.Print(); -} - static void PrintError(const Decorator &decorator, const DiagnosticsInfo &info) { const auto ErrorTypeStr = [&info]() -> const char * { @@ -91,5 +84,4 @@ void __rtsan::PrintDiagnostics(const DiagnosticsInfo &info) { PrintError(d, info); PrintReason(d, info); Printf("%s", d.Default()); - PrintStackTrace(info.pc, info.bp); } diff --git a/compiler-rt/lib/rtsan/rtsan_diagnostics.h b/compiler-rt/lib/rtsan/rtsan_diagnostics.h index f8a6b8a954a24..f12d0e85a5adf 100644 --- a/compiler-rt/lib/rtsan/rtsan_diagnostics.h +++ b/compiler-rt/lib/rtsan/rtsan_diagnostics.h @@ -25,8 +25,6 @@ enum class DiagnosticsInfoType { struct DiagnosticsInfo { DiagnosticsInfoType type; const char *func_name; - __sanitizer::uptr pc; - __sanitizer::uptr bp; }; void PrintDiagnostics(const DiagnosticsInfo &info); diff --git a/compiler-rt/lib/rtsan/rtsan_flags.inc b/compiler-rt/lib/rtsan/rtsan_flags.inc index 93b0294313672..1df71127d19d3 100644 --- a/compiler-rt/lib/rtsan/rtsan_flags.inc +++ b/compiler-rt/lib/rtsan/rtsan_flags.inc @@ -16,5 +16,5 @@ // RTSAN_FLAG(Type, Name, DefaultValue, Description) // See COMMON_FLAG in sanitizer_flags.inc for more details. -// Example flag, until we get a real one -// RTSAN_FLAG(bool, halt_on_error, true, "If true, halt the program on error") +RTSAN_FLAG(bool, halt_on_error, true, "Exit after first reported error.") +RTSAN_FLAG(bool, print_stats_on_exit, false, "Print stats on exit.") diff --git a/compiler-rt/lib/rtsan/rtsan_stats.cpp b/compiler-rt/lib/rtsan/rtsan_stats.cpp new file mode 100644 index 0000000000000..7c1ccf2876f08 --- /dev/null +++ b/compiler-rt/lib/rtsan/rtsan_stats.cpp @@ -0,0 +1,35 @@ +//===--- rtsan_stats.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 +// +//===----------------------------------------------------------------------===// +// +// Part of the RealtimeSanitizer runtime library +// +//===----------------------------------------------------------------------===// + +#include "rtsan/rtsan_stats.h" + +#include "sanitizer_common/sanitizer_atomic.h" +#include "sanitizer_common/sanitizer_common.h" + +using namespace __sanitizer; +using namespace __rtsan; + +static atomic_uint32_t rtsan_total_error_count{0}; + +void __rtsan::IncrementTotalErrorCount() { + atomic_fetch_add(&rtsan_total_error_count, 1, memory_order_relaxed); +} + +static u32 GetTotalErrorCount() { + return atomic_load(&rtsan_total_error_count, memory_order_relaxed); +} + +void __rtsan::PrintStatisticsSummary() { + ScopedErrorReportLock l; + Printf("RealtimeSanitizer exit stats:\n"); + Printf(" Total error count: %u\n", GetTotalErrorCount()); +} diff --git a/compiler-rt/lib/rtsan/rtsan_stats.h b/compiler-rt/lib/rtsan/rtsan_stats.h new file mode 100644 index 0000000000000..3aa30f6a5db76 --- /dev/null +++ b/compiler-rt/lib/rtsan/rtsan_stats.h @@ -0,0 +1,21 @@ +//===--- rtsan_stats.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 +// +//===----------------------------------------------------------------------===// +// +// Part of the RealtimeSanitizer runtime library +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace __rtsan { + +void IncrementTotalErrorCount(); + +void PrintStatisticsSummary(); + +} // namespace __rtsan diff --git a/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp b/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp index 58f7dbae96e9f..03007b39dfea2 100644 --- a/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp +++ b/compiler-rt/lib/rtsan/tests/rtsan_test_assertions.cpp @@ -17,6 +17,7 @@ #include using namespace __rtsan; +using namespace __sanitizer; class TestRtsanAssertions : public ::testing::Test { protected: @@ -25,9 +26,10 @@ class TestRtsanAssertions : public ::testing::Test { static void ExpectViolationAction(__rtsan::Context &context, bool expect_violation_callback) { - ::testing::MockFunction mock_on_violation; + ::testing::MockFunction mock_on_violation; EXPECT_CALL(mock_on_violation, Call).Times(expect_violation_callback ? 1 : 0); - ExpectNotRealtime(context, mock_on_violation.AsStdFunction()); + GET_CALLER_PC_BP; + ExpectNotRealtime(context, mock_on_violation.AsStdFunction(), pc, bp); } TEST_F(TestRtsanAssertions, diff --git a/compiler-rt/test/rtsan/exit_stats.cpp b/compiler-rt/test/rtsan/exit_stats.cpp new file mode 100644 index 0000000000000..b46a0fd62bac1 --- /dev/null +++ b/compiler-rt/test/rtsan/exit_stats.cpp @@ -0,0 +1,23 @@ +// RUN: %clangxx -fsanitize=realtime %s -o %t +// RUN: env RTSAN_OPTIONS="halt_on_error=false,print_stats_on_exit=true" %run %t 2>&1 | FileCheck %s + +// UNSUPPORTED: ios + +// Intent: Ensure exits stats are printed on exit. + +#include + +void violation() [[clang::nonblocking]] { + const int kNumViolations = 10; + for (int i = 0; i < kNumViolations; i++) { + usleep(1); + } +} + +int main() { + violation(); + return 0; +} + +// CHECK: RealtimeSanitizer exit stats: +// CHECK-NEXT: Total error count: 10 diff --git a/compiler-rt/test/rtsan/halt_on_error.cpp b/compiler-rt/test/rtsan/halt_on_error.cpp new file mode 100644 index 0000000000000..1d1d90e93f3f4 --- /dev/null +++ b/compiler-rt/test/rtsan/halt_on_error.cpp @@ -0,0 +1,25 @@ +// RUN: %clangxx -fsanitize=realtime %s -o %t +// RUN: env RTSAN_OPTIONS="halt_on_error=false" %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: ios + +// Intent: Ensure that halt_on_error does not exit on the first violation. + +#include + +void *MallocViolation() { return malloc(10); } + +void FreeViolation(void *Ptr) { free(Ptr); } + +void process() [[clang::nonblocking]] { + void *Ptr = MallocViolation(); + FreeViolation(Ptr); +} + +int main() { + process(); + return 0; + // CHECK: ==ERROR: RealtimeSanitizer + // CHECK-NEXT: {{.*`malloc`.*}} + // CHECK: ==ERROR: RealtimeSanitizer + // CHECK-NEXT: {{.*`free`.*}} +}