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(ScudoWrappersCppDeathTest, 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() && 116 scudo::systemSupportsMemoryTagging()) 117 NoTags = std::make_unique<scudo::ScopedDisableMemoryTagChecks>(); 118 119 Ready = false; 120 std::thread Threads[32]; 121 for (size_t I = 0U; I < sizeof(Threads) / sizeof(Threads[0]); I++) 122 Threads[I] = std::thread(stressNew); 123 { 124 std::unique_lock<std::mutex> Lock(Mutex); 125 Ready = true; 126 Cv.notify_all(); 127 } 128 for (auto &T : Threads) 129 T.join(); 130 } 131 132 #if !SCUDO_FUCHSIA 133 // TODO(kostyak): for me, this test fails in a specific configuration when ran 134 // by itself with some Scudo or GWP-ASan violation. Other people 135 // can't seem to reproduce the failure. Consider skipping this in 136 // the event it fails on the upstream bots. 137 TEST(ScudoWrappersCppTest, AllocAfterFork) { 138 std::atomic_bool Stop; 139 140 // Create threads that simply allocate and free different sizes. 141 std::vector<std::thread *> Threads; 142 for (size_t N = 0; N < 5; N++) { 143 std::thread *T = new std::thread([&Stop] { 144 while (!Stop) { 145 for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) { 146 char *P = new char[1UL << SizeLog]; 147 EXPECT_NE(P, nullptr); 148 // Make sure this value is not optimized away. 149 asm volatile("" : : "r,m"(P) : "memory"); 150 delete[] P; 151 } 152 } 153 }); 154 Threads.push_back(T); 155 } 156 157 // Create a thread to fork and allocate. 158 for (size_t N = 0; N < 100; N++) { 159 pid_t Pid; 160 if ((Pid = fork()) == 0) { 161 for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) { 162 char *P = new char[1UL << SizeLog]; 163 EXPECT_NE(P, nullptr); 164 // Make sure this value is not optimized away. 165 asm volatile("" : : "r,m"(P) : "memory"); 166 // Make sure we can touch all of the allocation. 167 memset(P, 0x32, 1U << SizeLog); 168 // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr)); 169 delete[] P; 170 } 171 _exit(10); 172 } 173 EXPECT_NE(-1, Pid); 174 int Status; 175 EXPECT_EQ(Pid, waitpid(Pid, &Status, 0)); 176 EXPECT_FALSE(WIFSIGNALED(Status)); 177 EXPECT_EQ(10, WEXITSTATUS(Status)); 178 } 179 180 printf("Waiting for threads to complete\n"); 181 Stop = true; 182 for (auto Thread : Threads) 183 Thread->join(); 184 Threads.clear(); 185 } 186 #endif 187