xref: /openbsd-src/gnu/llvm/compiler-rt/lib/scudo/standalone/tests/wrappers_cpp_test.cpp (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
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 // TODO(kostyak): for me, this test fails in a specific configuration when ran
134 //                by itself with some Scudo or GWP-ASan violation. Other people
135 //                can't seem to reproduce the failure. Consider skipping this in
136 //                the event it fails on the upstream bots.
137 TEST(ScudoWrappersCppTest, AllocAfterFork) {
138   std::atomic_bool Stop;
139 
140   // Create threads that simply allocate and free different sizes.
141   std::vector<std::thread *> Threads;
142   for (size_t N = 0; N < 5; N++) {
143     std::thread *T = new std::thread([&Stop] {
144       while (!Stop) {
145         for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) {
146           char *P = new char[1UL << SizeLog];
147           EXPECT_NE(P, nullptr);
148           // Make sure this value is not optimized away.
149           asm volatile("" : : "r,m"(P) : "memory");
150           delete[] P;
151         }
152       }
153     });
154     Threads.push_back(T);
155   }
156 
157   // Create a thread to fork and allocate.
158   for (size_t N = 0; N < 100; N++) {
159     pid_t Pid;
160     if ((Pid = fork()) == 0) {
161       for (size_t SizeLog = 3; SizeLog <= 21; SizeLog++) {
162         char *P = new char[1UL << SizeLog];
163         EXPECT_NE(P, nullptr);
164         // Make sure this value is not optimized away.
165         asm volatile("" : : "r,m"(P) : "memory");
166         // Make sure we can touch all of the allocation.
167         memset(P, 0x32, 1U << SizeLog);
168         // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr));
169         delete[] P;
170       }
171       _exit(10);
172     }
173     EXPECT_NE(-1, Pid);
174     int Status;
175     EXPECT_EQ(Pid, waitpid(Pid, &Status, 0));
176     EXPECT_FALSE(WIFSIGNALED(Status));
177     EXPECT_EQ(10, WEXITSTATUS(Status));
178   }
179 
180   printf("Waiting for threads to complete\n");
181   Stop = true;
182   for (auto Thread : Threads)
183     Thread->join();
184   Threads.clear();
185 }
186 #endif
187