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