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_interface_internal.h"
22#include "sanitizer_common/sanitizer_mutex.h"
23#include "sanitizer_common/sanitizer_stackdepot.h"
24
25using namespace __rtsan;
26using namespace __sanitizer;
27
28namespace {
29enum class InitializationState : u8 {
30 Uninitialized,
31 Initializing,
32 Initialized,
33};
34} // namespace
35
36static StaticSpinMutex rtsan_inited_mutex;
37static atomic_uint8_t rtsan_initialized = {
38 .val_dont_use: static_cast<u8>(InitializationState::Uninitialized)};
39
40static void SetInitializationState(InitializationState state) {
41 atomic_store(a: &rtsan_initialized, v: static_cast<u8>(state),
42 mo: memory_order_release);
43}
44
45static InitializationState GetInitializationState() {
46 return static_cast<InitializationState>(
47 atomic_load(a: &rtsan_initialized, mo: memory_order_acquire));
48}
49
50static void OnViolation(const BufferedStackTrace &stack,
51 const DiagnosticsInfo &info) {
52 IncrementTotalErrorCount();
53
54 // If in the future we interop with other sanitizers, we will
55 // need to make our own stackdepot
56 StackDepotHandle handle = StackDepotPut_WithHandle(stack);
57
58 const bool is_stack_novel = handle.use_count() == 0;
59 if (is_stack_novel || !flags().suppress_equal_stacks) {
60 IncrementUniqueErrorCount();
61
62 {
63 ScopedErrorReportLock l;
64 PrintDiagnostics(info);
65 stack.Print();
66 PrintErrorSummary(info, stack);
67 }
68
69 handle.inc_use_count_unsafe();
70 }
71
72 if (flags().halt_on_error) {
73 if (flags().print_stats_on_exit)
74 PrintStatisticsSummary();
75 Die();
76 }
77}
78
79extern "C" {
80
81SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_init() {
82 CHECK(GetInitializationState() == InitializationState::Uninitialized);
83 SetInitializationState(InitializationState::Initializing);
84
85 SanitizerToolName = "RealtimeSanitizer";
86 CacheBinaryName();
87 InitializeFlags();
88 __sanitizer_set_report_path(path: common_flags()->log_path);
89
90 InitializePlatformEarly();
91
92 InitializeInterceptors();
93
94 InitializeSuppressions();
95
96 if (flags().print_stats_on_exit)
97 Atexit(function: PrintStatisticsSummary);
98
99 SetInitializationState(InitializationState::Initialized);
100}
101
102SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_ensure_initialized() {
103 if (LIKELY(__rtsan_is_initialized()))
104 return;
105
106 SpinMutexLock lock(&rtsan_inited_mutex);
107
108 // Someone may have initialized us while we were waiting for the lock
109 if (__rtsan_is_initialized())
110 return;
111
112 __rtsan_init();
113}
114
115SANITIZER_INTERFACE_ATTRIBUTE bool __rtsan_is_initialized() {
116 return GetInitializationState() == InitializationState::Initialized;
117}
118
119SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() {
120 GetContextForThisThread().RealtimePush();
121}
122
123SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit() {
124 GetContextForThisThread().RealtimePop();
125}
126
127SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_disable() {
128 GetContextForThisThread().BypassPush();
129}
130
131SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable() {
132 GetContextForThisThread().BypassPop();
133}
134
135SANITIZER_INTERFACE_ATTRIBUTE void
136__rtsan_notify_intercepted_call(const char *func_name) {
137 // While initializing, we need all intercepted functions to behave normally
138 if (GetInitializationState() == InitializationState::Initializing)
139 return;
140
141 __rtsan_ensure_initialized();
142 GET_CALLER_PC_BP;
143 ExpectNotRealtime(context&: GetContextForThisThread(),
144 info: {.type: DiagnosticsInfoType::InterceptedCall, .func_name: func_name, .pc: pc, .bp: bp},
145 OnViolation);
146}
147
148SANITIZER_INTERFACE_ATTRIBUTE void
149__rtsan_notify_blocking_call(const char *func_name) {
150 __rtsan_ensure_initialized();
151 GET_CALLER_PC_BP;
152 ExpectNotRealtime(context&: GetContextForThisThread(),
153 info: {.type: DiagnosticsInfoType::BlockingCall, .func_name: func_name, .pc: pc, .bp: bp},
154 OnViolation);
155}
156
157SANITIZER_INTERFACE_ATTRIBUTE
158void __sanitizer_print_stack_trace() {
159 GET_CURRENT_PC_BP;
160 UNINITIALIZED BufferedStackTrace stack;
161 stack.Unwind(pc, bp, context: nullptr, request_fast: common_flags()->fast_unwind_on_fatal);
162 stack.Print();
163}
164
165} // extern "C"
166