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
24using namespace __rtsan;
25using namespace __sanitizer;
26
27namespace {
28enum class InitializationState : u8 {
29 Uninitialized,
30 Initializing,
31 Initialized,
32};
33} // namespace
34
35static StaticSpinMutex rtsan_inited_mutex;
36static atomic_uint8_t rtsan_initialized = {
37 .val_dont_use: static_cast<u8>(InitializationState::Uninitialized)};
38
39static void SetInitializationState(InitializationState state) {
40 atomic_store(a: &rtsan_initialized, v: static_cast<u8>(state),
41 mo: memory_order_release);
42}
43
44static InitializationState GetInitializationState() {
45 return static_cast<InitializationState>(
46 atomic_load(a: &rtsan_initialized, mo: memory_order_acquire));
47}
48
49static 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
78extern "C" {
79
80SANITIZER_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
99SANITIZER_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
112SANITIZER_INTERFACE_ATTRIBUTE bool __rtsan_is_initialized() {
113 return GetInitializationState() == InitializationState::Initialized;
114}
115
116SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_enter() {
117 GetContextForThisThread().RealtimePush();
118}
119
120SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_realtime_exit() {
121 GetContextForThisThread().RealtimePop();
122}
123
124SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_disable() {
125 GetContextForThisThread().BypassPush();
126}
127
128SANITIZER_INTERFACE_ATTRIBUTE void __rtsan_enable() {
129 GetContextForThisThread().BypassPop();
130}
131
132SANITIZER_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
145SANITIZER_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