xref: /llvm-project/compiler-rt/lib/rtsan/rtsan.cpp (revision 4bdac0851f4d613890558a8254043e21b0479b1e)
1 //===--- rtsan.cpp - Realtime Sanitizer -------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #include "rtsan/rtsan.h"
12 #include "rtsan/rtsan_assertions.h"
13 #include "rtsan/rtsan_diagnostics.h"
14 #include "rtsan/rtsan_flags.h"
15 #include "rtsan/rtsan_interceptors.h"
16 #include "rtsan/rtsan_stats.h"
17 #include "rtsan/rtsan_suppressions.h"
18 
19 #include "sanitizer_common/sanitizer_atomic.h"
20 #include "sanitizer_common/sanitizer_common.h"
21 #include "sanitizer_common/sanitizer_mutex.h"
22 #include "sanitizer_common/sanitizer_stackdepot.h"
23 
24 using namespace __rtsan;
25 using namespace __sanitizer;
26 
27 namespace {
28 enum class InitializationState : u8 {
29   Uninitialized,
30   Initializing,
31   Initialized,
32 };
33 } // namespace
34 
35 static StaticSpinMutex rtsan_inited_mutex;
36 static atomic_uint8_t rtsan_initialized = {
37     static_cast<u8>(InitializationState::Uninitialized)};
38 
39 static void SetInitializationState(InitializationState state) {
40   atomic_store(&rtsan_initialized, static_cast<u8>(state),
41                memory_order_release);
42 }
43 
44 static InitializationState GetInitializationState() {
45   return static_cast<InitializationState>(
46       atomic_load(&rtsan_initialized, memory_order_acquire));
47 }
48 
49 static void OnViolation(const BufferedStackTrace &stack,
50                         const DiagnosticsInfo &info) {
51   IncrementTotalErrorCount();
52 
53   // If in the future we interop with other sanitizers, we will
54   // need to make our own stackdepot
55   StackDepotHandle handle = StackDepotPut_WithHandle(stack);
56 
57   const bool is_stack_novel = handle.use_count() == 0;
58   if (is_stack_novel || !flags().suppress_equal_stacks) {
59     IncrementUniqueErrorCount();
60 
61     {
62       ScopedErrorReportLock l;
63       PrintDiagnostics(info);
64       stack.Print();
65       PrintErrorSummary(info, stack);
66     }
67 
68     handle.inc_use_count_unsafe();
69   }
70 
71   if (flags().halt_on_error) {
72     if (flags().print_stats_on_exit)
73       PrintStatisticsSummary();
74     Die();
75   }
76 }
77 
78 extern "C" {
79 
80 SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() {
81   CHECK(GetInitializationState() == InitializationState::Uninitialized);
82   SetInitializationState(InitializationState::Initializing);
83 
84   SanitizerToolName = "RealtimeSanitizer";
85   InitializeFlags();
86 
87   InitializePlatformEarly();
88 
89   InitializeInterceptors();
90 
91   InitializeSuppressions();
92 
93   if (flags().print_stats_on_exit)
94     Atexit(PrintStatisticsSummary);
95 
96   SetInitializationState(InitializationState::Initialized);
97 }
98 
99 SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_ensure_initialized() {
100   if (LIKELY(__rtsan_is_initialized()))
101     return;
102 
103   SpinMutexLock lock(&rtsan_inited_mutex);
104 
105   // Someone may have initialized us while we were waiting for the lock
106   if (__rtsan_is_initialized())
107     return;
108 
109   __rtsan_init();
110 }
111 
112 SANITIZER_INTERFACE_ATTRIBUTE bool __rtsan_is_initialized() {
113   return GetInitializationState() == InitializationState::Initialized;
114 }
115 
116 SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() {
117   GetContextForThisThread().RealtimePush();
118 }
119 
120 SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit() {
121   GetContextForThisThread().RealtimePop();
122 }
123 
124 SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_disable() {
125   GetContextForThisThread().BypassPush();
126 }
127 
128 SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable() {
129   GetContextForThisThread().BypassPop();
130 }
131 
132 SANITIZER_INTERFACE_ATTRIBUTE void
133 __rtsan_notify_intercepted_call(const char *func_name) {
134   // While initializing, we need all intercepted functions to behave normally
135   if (GetInitializationState() == InitializationState::Initializing)
136     return;
137 
138   __rtsan_ensure_initialized();
139   GET_CALLER_PC_BP;
140   ExpectNotRealtime(GetContextForThisThread(),
141                     {DiagnosticsInfoType::InterceptedCall, func_name, pc, bp},
142                     OnViolation);
143 }
144 
145 SANITIZER_INTERFACE_ATTRIBUTE void
146 __rtsan_notify_blocking_call(const char *func_name) {
147   __rtsan_ensure_initialized();
148   GET_CALLER_PC_BP;
149   ExpectNotRealtime(GetContextForThisThread(),
150                     {DiagnosticsInfoType::BlockingCall, func_name, pc, bp},
151                     OnViolation);
152 }
153 
154 } // extern "C"
155