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
40 using namespace testing;
41 using namespace rtsan_testing;
42 using namespace std::chrono_literals;
43
TEST(TestRtsan,VectorPushBackAllocationDiesWhenRealtime)44 TEST(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
TEST(TestRtsan,DestructionOfObjectOnHeapDiesWhenRealtime)53 TEST(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
TEST(TestRtsan,SleepingAThreadDiesWhenRealtime)62 TEST(TestRtsan, SleepingAThreadDiesWhenRealtime) {
63 auto Func = []() { std::this_thread::sleep_for(1us); };
64 ExpectRealtimeDeath(Func);
65 ExpectNonRealtimeSurvival(Func);
66 }
67
TEST(TestRtsan,YieldingDiesWhenRealtime)68 TEST(TestRtsan, YieldingDiesWhenRealtime) {
69 auto Func = []() { std::this_thread::yield(); };
70 ExpectRealtimeDeath(Func);
71 ExpectNonRealtimeSurvival(Func);
72 }
73
TEST(TestRtsan,IfstreamCreationDiesWhenRealtime)74 TEST(TestRtsan, IfstreamCreationDiesWhenRealtime) {
75 auto Func = []() { std::ifstream ifs{"./file.txt"}; };
76 ExpectRealtimeDeath(Func);
77 ExpectNonRealtimeSurvival(Func);
78 std::remove("./file.txt");
79 }
80
TEST(TestRtsan,OfstreamCreationDiesWhenRealtime)81 TEST(TestRtsan, OfstreamCreationDiesWhenRealtime) {
82 auto Func = []() { std::ofstream ofs{"./file.txt"}; };
83 ExpectRealtimeDeath(Func);
84 ExpectNonRealtimeSurvival(Func);
85 std::remove("./file.txt");
86 }
87
TEST(TestRtsan,LockingAMutexDiesWhenRealtime)88 TEST(TestRtsan, LockingAMutexDiesWhenRealtime) {
89 std::mutex mutex;
90 auto Func = [&]() { mutex.lock(); };
91 ExpectRealtimeDeath(Func);
92 ExpectNonRealtimeSurvival(Func);
93 }
94
TEST(TestRtsan,UnlockingAMutexDiesWhenRealtime)95 TEST(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
TEST(TestRtsan,LockingASharedMutexDiesWhenRealtime)105 TEST(TestRtsan, LockingASharedMutexDiesWhenRealtime) {
106 std::shared_mutex mutex;
107 auto Func = [&]() { mutex.lock(); };
108 ExpectRealtimeDeath(Func);
109 ExpectNonRealtimeSurvival(Func);
110 }
111
TEST(TestRtsan,UnlockingASharedMutexDiesWhenRealtime)112 TEST(TestRtsan, UnlockingASharedMutexDiesWhenRealtime) {
113 std::shared_mutex mutex;
114 mutex.lock();
115 auto Func = [&]() { mutex.unlock(); };
116 ExpectRealtimeDeath(Func);
117 ExpectNonRealtimeSurvival(Func);
118 }
119
TEST(TestRtsan,SharedLockingASharedMutexDiesWhenRealtime)120 TEST(TestRtsan, SharedLockingASharedMutexDiesWhenRealtime) {
121 std::shared_mutex mutex;
122 auto Func = [&]() { mutex.lock_shared(); };
123 ExpectRealtimeDeath(Func);
124 ExpectNonRealtimeSurvival(Func);
125 }
126
TEST(TestRtsan,SharedUnlockingASharedMutexDiesWhenRealtime)127 TEST(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
TEST(TestRtsan,LaunchingAThreadDiesWhenRealtime)137 TEST(TestRtsan, LaunchingAThreadDiesWhenRealtime) {
138 auto Func = [&]() {
139 std::thread Thread{[]() {}};
140 Thread.join();
141 };
142 ExpectRealtimeDeath(Func);
143 ExpectNonRealtimeSurvival(Func);
144 }
145
146 namespace {
InvokeStdFunction(std::function<void ()> && function)147 void InvokeStdFunction(std::function<void()> &&function) { function(); }
148
HideMemoryFromCompiler(T * memory)149 template <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
TEST(TestRtsan,CopyingALambdaWithLargeCaptureDiesWhenRealtime)157 TEST(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(&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
TEST(TestRtsan,AccessingALargeAtomicVariableDiesWhenRealtime)173 TEST(TestRtsan, AccessingALargeAtomicVariableDiesWhenRealtime) {
174 std::atomic<float> small_atomic{0.0f};
175 ASSERT_TRUE(small_atomic.is_lock_free());
176 RealtimeInvoke([&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
TEST(TestRtsan,FirstCoutDiesWhenRealtime)191 TEST(TestRtsan, FirstCoutDiesWhenRealtime) {
192 auto Func = []() { std::cout << "Hello, world!" << std::endl; };
193 ExpectRealtimeDeath(Func);
194 ExpectNonRealtimeSurvival(Func);
195 }
196
TEST(TestRtsan,SecondCoutDiesWhenRealtime)197 TEST(TestRtsan, SecondCoutDiesWhenRealtime) {
198 std::cout << "Hello, world";
199 auto Func = []() { std::cout << "Hello, again!" << std::endl; };
200 ExpectRealtimeDeath(Func);
201 ExpectNonRealtimeSurvival(Func);
202 }
203
TEST(TestRtsan,PrintfDiesWhenRealtime)204 TEST(TestRtsan, PrintfDiesWhenRealtime) {
205 auto Func = []() { printf("Hello, world!\n"); };
206 ExpectRealtimeDeath(Func);
207 ExpectNonRealtimeSurvival(Func);
208 }
209
TEST(TestRtsan,ThrowingAnExceptionDiesWhenRealtime)210 TEST(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
TEST(TestRtsan,DoesNotDieIfTurnedOff)221 TEST(TestRtsan, DoesNotDieIfTurnedOff) {
222 std::mutex mutex;
223 auto RealtimeBlockingFunc = [&]() {
224 __rtsan_disable();
225 mutex.lock();
226 mutex.unlock();
227 __rtsan_enable();
228 };
229 RealtimeInvoke(RealtimeBlockingFunc);
230 }
231