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