Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 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 | |
Vitaly Buka | b5ac593 | 2021-05-29 17:11:36 -0700 | [diff] [blame] | 9 | #include "memtag.h" |
Dynamic Tools Team | 09e6d48 | 2019-11-26 18:18:14 -0800 | [diff] [blame] | 10 | #include "tests/scudo_unit_test.h" |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 11 | |
Dynamic Tools Team | 83eaa51 | 2020-01-09 11:43:16 -0800 | [diff] [blame] | 12 | #include <atomic> |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 13 | #include <condition_variable> |
Vitaly Buka | c4311c7 | 2021-06-23 23:52:47 -0700 | [diff] [blame] | 14 | #include <memory> |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 15 | #include <mutex> |
| 16 | #include <thread> |
Dynamic Tools Team | 09e6d48 | 2019-11-26 18:18:14 -0800 | [diff] [blame] | 17 | #include <vector> |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 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 | |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 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) { |
Mitch Phillips | 3ae243e | 2021-05-10 12:19:19 -0700 | [diff] [blame] | 71 | if (getenv("SKIP_TYPE_MISMATCH")) { |
| 72 | printf("Skipped type mismatch tests.\n"); |
| 73 | return; |
| 74 | } |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 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; |
Kostya Kortchinsky | c72ca56 | 2020-07-27 09:13:42 -0700 | [diff] [blame] | 88 | static bool Ready; |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 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) { |
Vitaly Buka | b5ac593 | 2021-05-29 17:11:36 -0700 | [diff] [blame] | 112 | // TODO: Investigate why libc sometimes crashes with tag missmatch in |
| 113 | // __pthread_clockjoin_ex. |
Vitaly Buka | c4311c7 | 2021-06-23 23:52:47 -0700 | [diff] [blame] | 114 | std::unique_ptr<scudo::ScopedDisableMemoryTagChecks> NoTags; |
Vitaly Buka | b581108 | 2021-06-24 00:07:24 -0700 | [diff] [blame] | 115 | if (!SCUDO_ANDROID && scudo::archSupportsMemoryTagging() && scudo::systemSupportsMemoryTagging()) |
Vitaly Buka | c4311c7 | 2021-06-23 23:52:47 -0700 | [diff] [blame] | 116 | NoTags = std::make_unique<scudo::ScopedDisableMemoryTagChecks>(); |
Vitaly Buka | c971fbf | 2021-06-23 23:58:09 -0700 | [diff] [blame] | 117 | |
Kostya Kortchinsky | c72ca56 | 2020-07-27 09:13:42 -0700 | [diff] [blame] | 118 | Ready = false; |
Dynamic Tools Team | 517193e | 2019-09-11 14:48:41 +0000 | [diff] [blame] | 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 | } |
Dynamic Tools Team | 83eaa51 | 2020-01-09 11:43:16 -0800 | [diff] [blame] | 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 |