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 | .val_dont_use: static_cast<u8>(InitializationState::Uninitialized)}; |
38 | |
39 | static void SetInitializationState(InitializationState state) { |
40 | atomic_store(a: &rtsan_initialized, v: static_cast<u8>(state), |
41 | mo: memory_order_release); |
42 | } |
43 | |
44 | static InitializationState GetInitializationState() { |
45 | return static_cast<InitializationState>( |
46 | atomic_load(a: &rtsan_initialized, mo: 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(function: 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(context&: GetContextForThisThread(), |
141 | info: {.type: DiagnosticsInfoType::InterceptedCall, .func_name: func_name, .pc: pc, .bp: 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(context&: GetContextForThisThread(), |
150 | info: {.type: DiagnosticsInfoType::BlockingCall, .func_name: func_name, .pc: pc, .bp: bp}, |
151 | OnViolation); |
152 | } |
153 | |
154 | } // extern "C" |
155 | |