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
testCxxNew()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
TEST(ScudoWrappersCppTest,New)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
stressNew()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
TEST(ScudoWrappersCppTest,ThreadedNew)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.
TEST(ScudoWrappersCppTest,AllocAfterFork)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