1//===--- rtsan_test.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// Introduces basic functional tests for the realtime sanitizer.
10// Not meant to be exhaustive, testing all interceptors, please see
11// test_rtsan_interceptors.cpp for those tests.
12//
13//===----------------------------------------------------------------------===//
14
15#include "gtest/gtest.h"
16
17#include "rtsan_test_utilities.h"
18
19#include "rtsan/rtsan.h"
20#include "sanitizer_common/sanitizer_platform.h"
21#include "sanitizer_common/sanitizer_platform_interceptors.h"
22
23#include <array>
24#include <atomic>
25#include <chrono>
26#include <fstream>
27#include <mutex>
28#include <shared_mutex>
29#include <thread>
30
31#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
32 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101200
33#define SI_MAC_DEPLOYMENT_AT_LEAST_10_12 1
34#else
35#define SI_MAC_DEPLOYMENT_AT_LEAST_10_12 0
36#endif
37
38#define RTSAN_TEST_SHARED_MUTEX (!(SI_MAC) || SI_MAC_DEPLOYMENT_AT_LEAST_10_12)
39
40using namespace testing;
41using namespace rtsan_testing;
42using namespace std::chrono_literals;
43
44TEST(TestRtsan, VectorPushBackAllocationDiesWhenRealtime) {
45 std::vector<float> vec;
46 auto Func = [&vec]() { vec.push_back(0.4f); };
47 ExpectRealtimeDeath(Func);
48 ASSERT_EQ(0u, vec.size());
49 ExpectNonRealtimeSurvival(Func);
50 ASSERT_EQ(1u, vec.size());
51}
52
53TEST(TestRtsan, DestructionOfObjectOnHeapDiesWhenRealtime) {
54 auto allocated_ptr = std::make_unique<std::array<float, 256>>();
55 auto Func = [&allocated_ptr]() { allocated_ptr.reset(); };
56 ExpectRealtimeDeath(Func);
57 ASSERT_NE(nullptr, allocated_ptr.get());
58 ExpectNonRealtimeSurvival(Func);
59 ASSERT_EQ(nullptr, allocated_ptr.get());
60}
61
62TEST(TestRtsan, SleepingAThreadDiesWhenRealtime) {
63 auto Func = []() { std::this_thread::sleep_for(rtime: 1us); };
64 ExpectRealtimeDeath(Func);
65 ExpectNonRealtimeSurvival(Func);
66}
67
68TEST(TestRtsan, YieldingDiesWhenRealtime) {
69 auto Func = []() { std::this_thread::yield(); };
70 ExpectRealtimeDeath(Func);
71 ExpectNonRealtimeSurvival(Func);
72}
73
74TEST(TestRtsan, IfstreamCreationDiesWhenRealtime) {
75 auto Func = []() { std::ifstream ifs{"./file.txt"}; };
76 ExpectRealtimeDeath(Func);
77 ExpectNonRealtimeSurvival(Func);
78 std::remove(filename: "./file.txt");
79}
80
81TEST(TestRtsan, OfstreamCreationDiesWhenRealtime) {
82 auto Func = []() { std::ofstream ofs{"./file.txt"}; };
83 ExpectRealtimeDeath(Func);
84 ExpectNonRealtimeSurvival(Func);
85 std::remove(filename: "./file.txt");
86}
87
88TEST(TestRtsan, LockingAMutexDiesWhenRealtime) {
89 std::mutex mutex;
90 auto Func = [&]() { mutex.lock(); };
91 ExpectRealtimeDeath(Func);
92 ExpectNonRealtimeSurvival(Func);
93}
94
95TEST(TestRtsan, UnlockingAMutexDiesWhenRealtime) {
96 std::mutex mutex;
97 mutex.lock();
98 auto Func = [&]() { mutex.unlock(); };
99 ExpectRealtimeDeath(Func);
100 ExpectNonRealtimeSurvival(Func);
101}
102
103#if RTSAN_TEST_SHARED_MUTEX
104
105TEST(TestRtsan, LockingASharedMutexDiesWhenRealtime) {
106 std::shared_mutex mutex;
107 auto Func = [&]() { mutex.lock(); };
108 ExpectRealtimeDeath(Func);
109 ExpectNonRealtimeSurvival(Func);
110}
111
112TEST(TestRtsan, UnlockingASharedMutexDiesWhenRealtime) {
113 std::shared_mutex mutex;
114 mutex.lock();
115 auto Func = [&]() { mutex.unlock(); };
116 ExpectRealtimeDeath(Func);
117 ExpectNonRealtimeSurvival(Func);
118}
119
120TEST(TestRtsan, SharedLockingASharedMutexDiesWhenRealtime) {
121 std::shared_mutex mutex;
122 auto Func = [&]() { mutex.lock_shared(); };
123 ExpectRealtimeDeath(Func);
124 ExpectNonRealtimeSurvival(Func);
125}
126
127TEST(TestRtsan, SharedUnlockingASharedMutexDiesWhenRealtime) {
128 std::shared_mutex mutex;
129 mutex.lock_shared();
130 auto Func = [&]() { mutex.unlock_shared(); };
131 ExpectRealtimeDeath(Func);
132 ExpectNonRealtimeSurvival(Func);
133}
134
135#endif // RTSAN_TEST_SHARED_MUTEX
136
137TEST(TestRtsan, LaunchingAThreadDiesWhenRealtime) {
138 auto Func = [&]() {
139 std::thread Thread{[]() {}};
140 Thread.join();
141 };
142 ExpectRealtimeDeath(Func);
143 ExpectNonRealtimeSurvival(Func);
144}
145
146namespace {
147void InvokeStdFunction(std::function<void()> &&function) { function(); }
148
149template <typename T> void HideMemoryFromCompiler(T *memory) {
150 // Pass the pointer to an empty assembly block as an input, and inform
151 // the compiler that memory is read to and possibly modified. This should not
152 // be architecture specific, since the asm block is empty.
153 __asm__ __volatile__("" ::"r"(memory) : "memory");
154}
155} // namespace
156
157TEST(TestRtsan, CopyingALambdaWithLargeCaptureDiesWhenRealtime) {
158 std::array<float, 16> lots_of_data;
159 auto LargeLambda = [lots_of_data]() mutable {
160 lots_of_data[3] = 0.25f;
161 // In LTO builds, this lambda can be optimized away, since the compiler can
162 // see through the memory accesses after inlining across TUs. Ensure it can
163 // no longer reason about the memory access, so that won't happen.
164 HideMemoryFromCompiler(memory: &lots_of_data[3]);
165 EXPECT_EQ(16u, lots_of_data.size());
166 EXPECT_EQ(0.25f, lots_of_data[3]);
167 };
168 auto Func = [&]() { InvokeStdFunction(LargeLambda); };
169 ExpectRealtimeDeath(Func);
170 ExpectNonRealtimeSurvival(Func);
171}
172
173TEST(TestRtsan, AccessingALargeAtomicVariableDiesWhenRealtime) {
174 std::atomic<float> small_atomic{0.0f};
175 ASSERT_TRUE(small_atomic.is_lock_free());
176 RealtimeInvoke(Func: [&small_atomic]() {
177 float x = small_atomic.load();
178 return x;
179 });
180
181 std::atomic<std::array<float, 2048>> large_atomic;
182 ASSERT_FALSE(large_atomic.is_lock_free());
183 auto Func = [&]() {
184 std::array<float, 2048> x = large_atomic.load();
185 return x;
186 };
187 ExpectRealtimeDeath(Func);
188 ExpectNonRealtimeSurvival(Func);
189}
190
191TEST(TestRtsan, FirstCoutDiesWhenRealtime) {
192 auto Func = []() { std::cout << "Hello, world!" << std::endl; };
193 ExpectRealtimeDeath(Func);
194 ExpectNonRealtimeSurvival(Func);
195}
196
197TEST(TestRtsan, SecondCoutDiesWhenRealtime) {
198 std::cout << "Hello, world";
199 auto Func = []() { std::cout << "Hello, again!" << std::endl; };
200 ExpectRealtimeDeath(Func);
201 ExpectNonRealtimeSurvival(Func);
202}
203
204TEST(TestRtsan, PrintfDiesWhenRealtime) {
205 auto Func = []() { printf(format: "Hello, world!\n"); };
206 ExpectRealtimeDeath(Func);
207 ExpectNonRealtimeSurvival(Func);
208}
209
210TEST(TestRtsan, ThrowingAnExceptionDiesWhenRealtime) {
211 auto Func = [&]() {
212 try {
213 throw std::exception();
214 } catch (std::exception &) {
215 }
216 };
217 ExpectRealtimeDeath(Func);
218 ExpectNonRealtimeSurvival(Func);
219}
220
221TEST(TestRtsan, DoesNotDieIfTurnedOff) {
222 std::mutex mutex;
223 auto RealtimeBlockingFunc = [&]() {
224 __rtsan_disable();
225 mutex.lock();
226 mutex.unlock();
227 __rtsan_enable();
228 };
229 RealtimeInvoke(Func&: RealtimeBlockingFunc);
230}
231

source code of compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp