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 "tests/scudo_unit_test.h" 10 11 #include <atomic> 12 #include <condition_variable> 13 #include <mutex> 14 #include <thread> 15 #include <vector> 16 17 void operator delete(void *, size_t) noexcept; 18 void operator delete[](void *, size_t) noexcept; 19 20 // Note that every Cxx allocation function in the test binary will be fulfilled 21 // by Scudo. See the comment in the C counterpart of this file. 22 23 template <typename T> static void testCxxNew() { 24 T *P = new T; 25 EXPECT_NE(P, nullptr); 26 memset(P, 0x42, sizeof(T)); 27 EXPECT_DEATH(delete[] P, ""); 28 delete P; 29 EXPECT_DEATH(delete P, ""); 30 31 P = new T; 32 EXPECT_NE(P, nullptr); 33 memset(P, 0x42, sizeof(T)); 34 operator delete(P, sizeof(T)); 35 36 P = new (std::nothrow) T; 37 EXPECT_NE(P, nullptr); 38 memset(P, 0x42, sizeof(T)); 39 delete P; 40 41 const size_t N = 16U; 42 T *A = new T[N]; 43 EXPECT_NE(A, nullptr); 44 memset(A, 0x42, sizeof(T) * N); 45 EXPECT_DEATH(delete A, ""); 46 delete[] A; 47 EXPECT_DEATH(delete[] A, ""); 48 49 A = new T[N]; 50 EXPECT_NE(A, nullptr); 51 memset(A, 0x42, sizeof(T) * N); 52 operator delete[](A, sizeof(T) * N); 53 54 A = new (std::nothrow) T[N]; 55 EXPECT_NE(A, nullptr); 56 memset(A, 0x42, sizeof(T) * N); 57 delete[] A; 58 } 59 60 class Pixel { 61 public: 62 enum class Color { Red, Green, Blue }; 63 int X = 0; 64 int Y = 0; 65 Color C = Color::Red; 66 }; 67 68 TEST(ScudoWrappersCppTest, New) { 69 if (getenv("SKIP_TYPE_MISMATCH")) { 70 printf("Skipped type mismatch tests.\n"); 71 return; 72 } 73 testCxxNew<bool>(); 74 testCxxNew<uint8_t>(); 75 testCxxNew<uint16_t>(); 76 testCxxNew<uint32_t>(); 77 testCxxNew<uint64_t>(); 78 testCxxNew<float>(); 79 testCxxNew<double>(); 80 testCxxNew<long double>(); 81 testCxxNew<Pixel>(); 82 } 83 84 static std::mutex Mutex; 85 static std::condition_variable Cv; 86 static bool Ready; 87 88 static void stressNew() { 89 std::vector<uintptr_t *> V; 90 { 91 std::unique_lock<std::mutex> Lock(Mutex); 92 while (!Ready) 93 Cv.wait(Lock); 94 } 95 for (size_t I = 0; I < 256U; I++) { 96 const size_t N = std::rand() % 128U; 97 uintptr_t *P = new uintptr_t[N]; 98 if (P) { 99 memset(P, 0x42, sizeof(uintptr_t) * N); 100 V.push_back(P); 101 } 102 } 103 while (!V.empty()) { 104 delete[] V.back(); 105 V.pop_back(); 106 } 107 } 108 109 TEST(ScudoWrappersCppTest, ThreadedNew) { 110 Ready = false; 111 std::thread Threads[32]; 112 for (size_t I = 0U; I < sizeof(Threads) / sizeof(Threads[0]); I++) 113 Threads[I] = std::thread(stressNew); 114 { 115 std::unique_lock<std::mutex> Lock(Mutex); 116 Ready = true; 117 Cv.notify_all(); 118 } 119 for (auto &T : Threads) 120 T.join(); 121 } 122 123 #if !SCUDO_FUCHSIA 124 // TODO(kostyak): for me, this test fails in a specific configuration when ran 125 // by itself with some Scudo or GWP-ASan violation. Other people 126 // can't seem to reproduce the failure. Consider skipping this in 127 // the event it fails on the upstream bots. 128 TEST(ScudoWrappersCppTest, AllocAfterFork) { 129 std::atomic_bool Stop; 130 131 // Create threads that simply allocate and free different sizes. 132 std::vector<std::thread *> Threads; 133 for (size_t N = 0; N < 5; N++) { 134 std::thread *T = new std::thread([&Stop] { 135 while (!Stop) { 136 for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) { 137 char *P = new char[1UL << SizeLog]; 138 EXPECT_NE(P, nullptr); 139 // Make sure this value is not optimized away. 140 asm volatile("" : : "r,m"(P) : "memory"); 141 delete[] P; 142 } 143 } 144 }); 145 Threads.push_back(T); 146 } 147 148 // Create a thread to fork and allocate. 149 for (size_t N = 0; N < 100; N++) { 150 pid_t Pid; 151 if ((Pid = fork()) == 0) { 152 for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) { 153 char *P = new char[1UL << SizeLog]; 154 EXPECT_NE(P, nullptr); 155 // Make sure this value is not optimized away. 156 asm volatile("" : : "r,m"(P) : "memory"); 157 // Make sure we can touch all of the allocation. 158 memset(P, 0x32, 1U << SizeLog); 159 // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr)); 160 delete[] P; 161 } 162 _exit(10); 163 } 164 EXPECT_NE(-1, Pid); 165 int Status; 166 EXPECT_EQ(Pid, waitpid(Pid, &Status, 0)); 167 EXPECT_FALSE(WIFSIGNALED(Status)); 168 EXPECT_EQ(10, WEXITSTATUS(Status)); 169 } 170 171 printf("Waiting for threads to complete\n"); 172 Stop = true; 173 for (auto Thread : Threads) 174 Thread->join(); 175 Threads.clear(); 176 } 177 #endif 178