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