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 "gtest/gtest.h"
10 
11 #include <condition_variable>
12 #include <mutex>
13 #include <thread>
14 
15 void operator delete(void *, size_t) noexcept;
16 void operator delete[](void *, size_t) noexcept;
17 
18 // Note that every Cxx allocation function in the test binary will be fulfilled
19 // by Scudo. See the comment in the C counterpart of this file.
20 
21 extern "C" __attribute__((visibility("default"))) const char *
22 __scudo_default_options() {
23   return "quarantine_size_kb=256:thread_local_quarantine_size_kb=128:"
24          "quarantine_max_chunk_size=512:dealloc_type_mismatch=true";
25 }
26 
27 template <typename T> static void testCxxNew() {
28   T *P = new T;
29   EXPECT_NE(P, nullptr);
30   memset(P, 0x42, sizeof(T));
31   EXPECT_DEATH(delete[] P, "");
32   delete P;
33   EXPECT_DEATH(delete P, "");
34 
35   P = new T;
36   EXPECT_NE(P, nullptr);
37   memset(P, 0x42, sizeof(T));
38   operator delete(P, sizeof(T));
39 
40   P = new (std::nothrow) T;
41   EXPECT_NE(P, nullptr);
42   memset(P, 0x42, sizeof(T));
43   delete P;
44 
45   const size_t N = 16U;
46   T *A = new T[N];
47   EXPECT_NE(A, nullptr);
48   memset(A, 0x42, sizeof(T) * N);
49   EXPECT_DEATH(delete A, "");
50   delete[] A;
51   EXPECT_DEATH(delete[] A, "");
52 
53   A = new T[N];
54   EXPECT_NE(A, nullptr);
55   memset(A, 0x42, sizeof(T) * N);
56   operator delete[](A, sizeof(T) * N);
57 
58   A = new (std::nothrow) T[N];
59   EXPECT_NE(A, nullptr);
60   memset(A, 0x42, sizeof(T) * N);
61   delete[] A;
62 }
63 
64 class Pixel {
65 public:
66   enum class Color { Red, Green, Blue };
67   int X = 0;
68   int Y = 0;
69   Color C = Color::Red;
70 };
71 
72 TEST(ScudoWrappersCppTest, New) {
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 = false;
87 
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 
109 TEST(ScudoWrappersCppTest, ThreadedNew) {
110   std::thread Threads[32];
111   for (size_t I = 0U; I < sizeof(Threads) / sizeof(Threads[0]); I++)
112     Threads[I] = std::thread(stressNew);
113   {
114     std::unique_lock<std::mutex> Lock(Mutex);
115     Ready = true;
116     Cv.notify_all();
117   }
118   for (auto &T : Threads)
119     T.join();
120 }
121