From aed8bc96f180f2164541a2cf307a09cb34a2e5d1 Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Fri, 16 Aug 2024 10:44:45 -0700 Subject: [PATCH 1/4] [rtsan] Introduce halt_on_error flag --- compiler-rt/lib/rtsan/rtsan.cpp | 19 +++++++++--------- compiler-rt/lib/rtsan/rtsan_flags.inc | 3 +-- compiler-rt/test/rtsan/halt_on_error.cpp | 25 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) create mode 100644 compiler-rt/test/rtsan/halt_on_error.cpp diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index b288da64ffbe2..dab0e453b56f9 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -29,10 +29,11 @@ static void SetInitialized() { atomic_store(&rtsan_initialized, 1, memory_order_release); } -static auto PrintDiagnosticsAndDieAction(DiagnosticsInfo info) { +static auto DefaultErrorAction(DiagnosticsInfo info) { return [info]() { __rtsan::PrintDiagnostics(info); - Die(); + if (flags().halt_on_error) + Die(); }; } @@ -85,20 +86,18 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_notify_intercepted_call(const char *func_name) { __rtsan_ensure_initialized(); GET_CALLER_PC_BP; - ExpectNotRealtime( - GetContextForThisThread(), - PrintDiagnosticsAndDieAction( - {DiagnosticsInfoType::InterceptedCall, func_name, pc, bp})); + ExpectNotRealtime(GetContextForThisThread(), + DefaultErrorAction({DiagnosticsInfoType::InterceptedCall, + func_name, pc, bp})); } SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_notify_blocking_call(const char *func_name) { __rtsan_ensure_initialized(); GET_CALLER_PC_BP; - ExpectNotRealtime( - GetContextForThisThread(), - PrintDiagnosticsAndDieAction( - {DiagnosticsInfoType::BlockingCall, func_name, pc, bp})); + ExpectNotRealtime(GetContextForThisThread(), + DefaultErrorAction({DiagnosticsInfoType::BlockingCall, + func_name, pc, bp})); } } // extern "C" diff --git a/compiler-rt/lib/rtsan/rtsan_flags.inc b/compiler-rt/lib/rtsan/rtsan_flags.inc index 93b0294313672..25d62cf0a60fb 100644 --- a/compiler-rt/lib/rtsan/rtsan_flags.inc +++ b/compiler-rt/lib/rtsan/rtsan_flags.inc @@ -16,5 +16,4 @@ // 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.") 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`.*}} +} From 324a86961e74dc1bf8997e93429e47ec3dce9ea5 Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Tue, 24 Sep 2024 09:10:49 -0700 Subject: [PATCH 2/4] [rtsan] Refactor initialization state to prevent hangs --- compiler-rt/lib/rtsan/rtsan.cpp | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index dab0e453b56f9..bf6f222fe2a0c 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -22,16 +22,31 @@ 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 DefaultErrorAction(DiagnosticsInfo info) { return [info]() { - __rtsan::PrintDiagnostics(info); + PrintDiagnostics(info); + if (flags().halt_on_error) Die(); }; @@ -40,13 +55,14 @@ static auto DefaultErrorAction(DiagnosticsInfo info) { extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() { - CHECK(!__rtsan_is_initialized()); + CHECK(GetInitializationState() == InitializationState::Uninitialized); + SetInitializationState(InitializationState::Initializing); SanitizerToolName = "RealtimeSanitizer"; InitializeFlags(); InitializeInterceptors(); - SetInitialized(); + SetInitializationState(InitializationState::Initialized); } SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_ensure_initialized() { @@ -63,7 +79,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() { @@ -84,6 +100,10 @@ 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(), From e63525c5c3fe162e584145628dc48ca3ff8c50a7 Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Fri, 13 Sep 2024 14:38:35 -0600 Subject: [PATCH 3/4] [rtsan] Add exit statistics --- compiler-rt/lib/rtsan/CMakeLists.txt | 8 ++++-- compiler-rt/lib/rtsan/rtsan.cpp | 9 +++++++ compiler-rt/lib/rtsan/rtsan_flags.inc | 1 + compiler-rt/lib/rtsan/rtsan_stats.cpp | 35 +++++++++++++++++++++++++++ compiler-rt/lib/rtsan/rtsan_stats.h | 21 ++++++++++++++++ compiler-rt/test/rtsan/exit_stats.cpp | 23 ++++++++++++++++++ 6 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 compiler-rt/lib/rtsan/rtsan_stats.cpp create mode 100644 compiler-rt/lib/rtsan/rtsan_stats.h create mode 100644 compiler-rt/test/rtsan/exit_stats.cpp 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 bf6f222fe2a0c..9c13987ed238f 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" @@ -43,8 +44,13 @@ static InitializationState GetInitializationState() { atomic_load(&rtsan_initialized, memory_order_acquire)); } +static void RtsanAtexit() { PrintStatisticsSummary(); } + static auto DefaultErrorAction(DiagnosticsInfo info) { return [info]() { + if (flags().print_stats_on_exit) + IncrementTotalErrorCount(); + PrintDiagnostics(info); if (flags().halt_on_error) @@ -62,6 +68,9 @@ SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() { InitializeFlags(); InitializeInterceptors(); + if (flags().print_stats_on_exit) + Atexit(RtsanAtexit); + SetInitializationState(InitializationState::Initialized); } diff --git a/compiler-rt/lib/rtsan/rtsan_flags.inc b/compiler-rt/lib/rtsan/rtsan_flags.inc index 25d62cf0a60fb..1df71127d19d3 100644 --- a/compiler-rt/lib/rtsan/rtsan_flags.inc +++ b/compiler-rt/lib/rtsan/rtsan_flags.inc @@ -17,3 +17,4 @@ // See COMMON_FLAG in sanitizer_flags.inc for more details. 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/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 From 87bc32becf3ef817b42b6463e0f932cec0ea1fb0 Mon Sep 17 00:00:00 2001 From: Chris Apple Date: Sun, 22 Sep 2024 18:29:51 -0600 Subject: [PATCH 4/4] [rtsan] Refactor where we get the stack Necessary for deduplicating efficiently --- compiler-rt/lib/rtsan/rtsan.cpp | 17 ++++++++++------- compiler-rt/lib/rtsan/rtsan_assertions.h | 14 ++++++++++++-- compiler-rt/lib/rtsan/rtsan_diagnostics.cpp | 8 -------- compiler-rt/lib/rtsan/rtsan_diagnostics.h | 2 -- .../lib/rtsan/tests/rtsan_test_assertions.cpp | 6 ++++-- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/compiler-rt/lib/rtsan/rtsan.cpp b/compiler-rt/lib/rtsan/rtsan.cpp index 9c13987ed238f..f688b06886720 100644 --- a/compiler-rt/lib/rtsan/rtsan.cpp +++ b/compiler-rt/lib/rtsan/rtsan.cpp @@ -47,11 +47,12 @@ static InitializationState GetInitializationState() { static void RtsanAtexit() { PrintStatisticsSummary(); } static auto DefaultErrorAction(DiagnosticsInfo info) { - return [info]() { + return [info](const BufferedStackTrace &stack) { if (flags().print_stats_on_exit) IncrementTotalErrorCount(); PrintDiagnostics(info); + stack.Print(); if (flags().halt_on_error) Die(); @@ -115,18 +116,20 @@ __rtsan_notify_intercepted_call(const char *func_name) { __rtsan_ensure_initialized(); GET_CALLER_PC_BP; - ExpectNotRealtime(GetContextForThisThread(), - DefaultErrorAction({DiagnosticsInfoType::InterceptedCall, - func_name, pc, bp})); + ExpectNotRealtime( + GetContextForThisThread(), + DefaultErrorAction({DiagnosticsInfoType::InterceptedCall, func_name}), pc, + bp); } SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_notify_blocking_call(const char *func_name) { __rtsan_ensure_initialized(); GET_CALLER_PC_BP; - ExpectNotRealtime(GetContextForThisThread(), - DefaultErrorAction({DiagnosticsInfoType::BlockingCall, - func_name, pc, bp})); + ExpectNotRealtime( + GetContextForThisThread(), + 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/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,