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 TEST(ScudoWrappersCppTest, AllocAfterFork) {
134   std::atomic_bool Stop;
135 
136   // Create threads that simply allocate and free different sizes.
137   std::vector<std::thread *> Threads;
138   for (size_t N = 0; N < 5; N++) {
139     std::thread *T = new std::thread([&Stop] {
140       while (!Stop) {
141         for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) {
142           char *P = new char[1UL << SizeLog];
143           EXPECT_NE(P, nullptr);
144           // Make sure this value is not optimized away.
145           asm volatile("" : : "r,m"(P) : "memory");
146           delete[] P;
147         }
148       }
149     });
150     Threads.push_back(T);
151   }
152 
153   // Create a thread to fork and allocate.
154   for (size_t N = 0; N < 50; N++) {
155     pid_t Pid;
156     if ((Pid = fork()) == 0) {
157       for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) {
158         char *P = new char[1UL << SizeLog];
159         EXPECT_NE(P, nullptr);
160         // Make sure this value is not optimized away.
161         asm volatile("" : : "r,m"(P) : "memory");
162         // Make sure we can touch all of the allocation.
163         memset(P, 0x32, 1U << SizeLog);
164         // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr));
165         delete[] P;
166       }
167       _exit(10);
168     }
169     EXPECT_NE(-1, Pid);
170     int Status;
171     EXPECT_EQ(Pid, waitpid(Pid, &Status, 0));
172     EXPECT_FALSE(WIFSIGNALED(Status));
173     EXPECT_EQ(10, WEXITSTATUS(Status));
174   }
175 
176   printf("Waiting for threads to complete\n");
177   Stop = true;
178   for (auto Thread : Threads)
179     Thread->join();
180   Threads.clear();
181 }
182 #endif
183