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 testCxxNew<bool>(); 70 testCxxNew<uint8_t>(); 71 testCxxNew<uint16_t>(); 72 testCxxNew<uint32_t>(); 73 testCxxNew<uint64_t>(); 74 testCxxNew<float>(); 75 testCxxNew<double>(); 76 testCxxNew<long double>(); 77 testCxxNew<Pixel>(); 78 } 79 80 static std::mutex Mutex; 81 static std::condition_variable Cv; 82 static bool Ready; 83 84 static void stressNew() { 85 std::vector<uintptr_t *> V; 86 { 87 std::unique_lock<std::mutex> Lock(Mutex); 88 while (!Ready) 89 Cv.wait(Lock); 90 } 91 for (size_t I = 0; I < 256U; I++) { 92 const size_t N = std::rand() % 128U; 93 uintptr_t *P = new uintptr_t[N]; 94 if (P) { 95 memset(P, 0x42, sizeof(uintptr_t) * N); 96 V.push_back(P); 97 } 98 } 99 while (!V.empty()) { 100 delete[] V.back(); 101 V.pop_back(); 102 } 103 } 104 105 TEST(ScudoWrappersCppTest, ThreadedNew) { 106 Ready = false; 107 std::thread Threads[32]; 108 for (size_t I = 0U; I < sizeof(Threads) / sizeof(Threads[0]); I++) 109 Threads[I] = std::thread(stressNew); 110 { 111 std::unique_lock<std::mutex> Lock(Mutex); 112 Ready = true; 113 Cv.notify_all(); 114 } 115 for (auto &T : Threads) 116 T.join(); 117 } 118 119 #if !SCUDO_FUCHSIA 120 // TODO(kostyak): for me, this test fails in a specific configuration when ran 121 // by itself with some Scudo or GWP-ASan violation. Other people 122 // can't seem to reproduce the failure. Consider skipping this in 123 // the event it fails on the upstream bots. 124 TEST(ScudoWrappersCppTest, AllocAfterFork) { 125 std::atomic_bool Stop; 126 127 // Create threads that simply allocate and free different sizes. 128 std::vector<std::thread *> Threads; 129 for (size_t N = 0; N < 5; N++) { 130 std::thread *T = new std::thread([&Stop] { 131 while (!Stop) { 132 for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) { 133 char *P = new char[1UL << SizeLog]; 134 EXPECT_NE(P, nullptr); 135 // Make sure this value is not optimized away. 136 asm volatile("" : : "r,m"(P) : "memory"); 137 delete[] P; 138 } 139 } 140 }); 141 Threads.push_back(T); 142 } 143 144 // Create a thread to fork and allocate. 145 for (size_t N = 0; N < 100; N++) { 146 pid_t Pid; 147 if ((Pid = fork()) == 0) { 148 for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) { 149 char *P = new char[1UL << SizeLog]; 150 EXPECT_NE(P, nullptr); 151 // Make sure this value is not optimized away. 152 asm volatile("" : : "r,m"(P) : "memory"); 153 // Make sure we can touch all of the allocation. 154 memset(P, 0x32, 1U << SizeLog); 155 // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr)); 156 delete[] P; 157 } 158 _exit(10); 159 } 160 EXPECT_NE(-1, Pid); 161 int Status; 162 EXPECT_EQ(Pid, waitpid(Pid, &Status, 0)); 163 EXPECT_FALSE(WIFSIGNALED(Status)); 164 EXPECT_EQ(10, WEXITSTATUS(Status)); 165 } 166 167 printf("Waiting for threads to complete\n"); 168 Stop = true; 169 for (auto Thread : Threads) 170 Thread->join(); 171 Threads.clear(); 172 } 173 #endif 174