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