1 //===-- wrappers_cpp_test.cpp -----------------------------------*- 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 #include "memtag.h" 10 #include "tests/scudo_unit_test.h" 11 12 #include <atomic> 13 #include <condition_variable> 14 #include <fstream> 15 #include <memory> 16 #include <mutex> 17 #include <thread> 18 #include <vector> 19 20 void operator delete(void *, size_t) noexcept; 21 void operator delete[](void *, size_t) noexcept; 22 23 // Note that every Cxx allocation function in the test binary will be fulfilled 24 // by Scudo. See the comment in the C counterpart of this file. 25 26 template <typename T> static void testCxxNew() { 27 T *P = new T; 28 EXPECT_NE(P, nullptr); 29 memset(P, 0x42, sizeof(T)); 30 EXPECT_DEATH(delete[] P, ""); 31 delete P; 32 EXPECT_DEATH(delete P, ""); 33 34 P = new T; 35 EXPECT_NE(P, nullptr); 36 memset(P, 0x42, sizeof(T)); 37 operator delete(P, sizeof(T)); 38 39 P = new (std::nothrow) T; 40 EXPECT_NE(P, nullptr); 41 memset(P, 0x42, sizeof(T)); 42 delete P; 43 44 const size_t N = 16U; 45 T *A = new T[N]; 46 EXPECT_NE(A, nullptr); 47 memset(A, 0x42, sizeof(T) * N); 48 EXPECT_DEATH(delete A, ""); 49 delete[] A; 50 EXPECT_DEATH(delete[] A, ""); 51 52 A = new T[N]; 53 EXPECT_NE(A, nullptr); 54 memset(A, 0x42, sizeof(T) * N); 55 operator delete[](A, sizeof(T) * N); 56 57 A = new (std::nothrow) T[N]; 58 EXPECT_NE(A, nullptr); 59 memset(A, 0x42, sizeof(T) * N); 60 delete[] A; 61 } 62 63 class Pixel { 64 public: 65 enum class Color { Red, Green, Blue }; 66 int X = 0; 67 int Y = 0; 68 Color C = Color::Red; 69 }; 70 71 TEST(ScudoWrappersCppDeathTest, New) { 72 if (getenv("SKIP_TYPE_MISMATCH")) { 73 printf("Skipped type mismatch tests.\n"); 74 return; 75 } 76 testCxxNew<bool>(); 77 testCxxNew<uint8_t>(); 78 testCxxNew<uint16_t>(); 79 testCxxNew<uint32_t>(); 80 testCxxNew<uint64_t>(); 81 testCxxNew<float>(); 82 testCxxNew<double>(); 83 testCxxNew<long double>(); 84 testCxxNew<Pixel>(); 85 } 86 87 static std::mutex Mutex; 88 static std::condition_variable Cv; 89 static bool Ready; 90 91 static void stressNew() { 92 std::vector<uintptr_t *> V; 93 { 94 std::unique_lock<std::mutex> Lock(Mutex); 95 while (!Ready) 96 Cv.wait(Lock); 97 } 98 for (size_t I = 0; I < 256U; I++) { 99 const size_t N = std::rand() % 128U; 100 uintptr_t *P = new uintptr_t[N]; 101 if (P) { 102 memset(P, 0x42, sizeof(uintptr_t) * N); 103 V.push_back(P); 104 } 105 } 106 while (!V.empty()) { 107 delete[] V.back(); 108 V.pop_back(); 109 } 110 } 111 112 TEST(ScudoWrappersCppTest, ThreadedNew) { 113 // TODO: Investigate why libc sometimes crashes with tag missmatch in 114 // __pthread_clockjoin_ex. 115 std::unique_ptr<scudo::ScopedDisableMemoryTagChecks> NoTags; 116 if (!SCUDO_ANDROID && scudo::archSupportsMemoryTagging() && 117 scudo::systemSupportsMemoryTagging()) 118 NoTags = std::make_unique<scudo::ScopedDisableMemoryTagChecks>(); 119 120 Ready = false; 121 std::thread Threads[32]; 122 for (size_t I = 0U; I < sizeof(Threads) / sizeof(Threads[0]); I++) 123 Threads[I] = std::thread(stressNew); 124 { 125 std::unique_lock<std::mutex> Lock(Mutex); 126 Ready = true; 127 Cv.notify_all(); 128 } 129 for (auto &T : Threads) 130 T.join(); 131 } 132 133 #if !SCUDO_FUCHSIA 134 TEST(ScudoWrappersCppTest, AllocAfterFork) { 135 // This test can fail flakily when ran as a part of large number of 136 // other tests if the maxmimum number of mappings allowed is low. 137 // We tried to reduce the number of iterations of the loops with 138 // moderate success, so we will now skip this test under those 139 // circumstances. 140 if (SCUDO_LINUX) { 141 long MaxMapCount = 0; 142 // If the file can't be accessed, we proceed with the test. 143 std::ifstream Stream("/proc/sys/vm/max_map_count"); 144 if (Stream.good()) { 145 Stream >> MaxMapCount; 146 if (MaxMapCount < 200000) 147 return; 148 } 149 } 150 151 std::atomic_bool Stop; 152 153 // Create threads that simply allocate and free different sizes. 154 std::vector<std::thread *> Threads; 155 for (size_t N = 0; N < 5; N++) { 156 std::thread *T = new std::thread([&Stop] { 157 while (!Stop) { 158 for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) { 159 char *P = new char[1UL << SizeLog]; 160 EXPECT_NE(P, nullptr); 161 // Make sure this value is not optimized away. 162 asm volatile("" : : "r,m"(P) : "memory"); 163 delete[] P; 164 } 165 } 166 }); 167 Threads.push_back(T); 168 } 169 170 // Create a thread to fork and allocate. 171 for (size_t N = 0; N < 50; N++) { 172 pid_t Pid; 173 if ((Pid = fork()) == 0) { 174 for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) { 175 char *P = new char[1UL << SizeLog]; 176 EXPECT_NE(P, nullptr); 177 // Make sure this value is not optimized away. 178 asm volatile("" : : "r,m"(P) : "memory"); 179 // Make sure we can touch all of the allocation. 180 memset(P, 0x32, 1U << SizeLog); 181 // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr)); 182 delete[] P; 183 } 184 _exit(10); 185 } 186 EXPECT_NE(-1, Pid); 187 int Status; 188 EXPECT_EQ(Pid, waitpid(Pid, &Status, 0)); 189 EXPECT_FALSE(WIFSIGNALED(Status)); 190 EXPECT_EQ(10, WEXITSTATUS(Status)); 191 } 192 193 printf("Waiting for threads to complete\n"); 194 Stop = true; 195 for (auto Thread : Threads) 196 Thread->join(); 197 Threads.clear(); 198 } 199 #endif 200