xref: /llvm-project/compiler-rt/lib/rtsan/tests/rtsan_test_functional.cpp (revision 44d9beef7d28f4a4d73acb12ea030bb642eff1d2)
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 
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 
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 
62 TEST(TestRtsan, SleepingAThreadDiesWhenRealtime) {
63   auto Func = []() { std::this_thread::sleep_for(1us); };
64   ExpectRealtimeDeath(Func);
65   ExpectNonRealtimeSurvival(Func);
66 }
67 
68 TEST(TestRtsan, YieldingDiesWhenRealtime) {
69   auto Func = []() { std::this_thread::yield(); };
70   ExpectRealtimeDeath(Func);
71   ExpectNonRealtimeSurvival(Func);
72 }
73 
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 
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 
88 TEST(TestRtsan, LockingAMutexDiesWhenRealtime) {
89   std::mutex mutex;
90   auto Func = [&]() { mutex.lock(); };
91   ExpectRealtimeDeath(Func);
92   ExpectNonRealtimeSurvival(Func);
93 }
94 
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 
105 TEST(TestRtsan, LockingASharedMutexDiesWhenRealtime) {
106   std::shared_mutex mutex;
107   auto Func = [&]() { mutex.lock(); };
108   ExpectRealtimeDeath(Func);
109   ExpectNonRealtimeSurvival(Func);
110 }
111 
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 
120 TEST(TestRtsan, SharedLockingASharedMutexDiesWhenRealtime) {
121   std::shared_mutex mutex;
122   auto Func = [&]() { mutex.lock_shared(); };
123   ExpectRealtimeDeath(Func);
124   ExpectNonRealtimeSurvival(Func);
125 }
126 
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 
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 {
147 void InvokeStdFunction(std::function<void()> &&function) { function(); }
148 
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 
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 
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 
191 TEST(TestRtsan, FirstCoutDiesWhenRealtime) {
192   auto Func = []() { std::cout << "Hello, world!" << std::endl; };
193   ExpectRealtimeDeath(Func);
194   ExpectNonRealtimeSurvival(Func);
195 }
196 
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 
204 TEST(TestRtsan, PrintfDiesWhenRealtime) {
205   auto Func = []() { printf("Hello, world!\n"); };
206   ExpectRealtimeDeath(Func);
207   ExpectNonRealtimeSurvival(Func);
208 }
209 
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 
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