xref: /openbsd-src/gnu/llvm/compiler-rt/lib/scudo/standalone/tests/wrappers_cpp_test.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- wrappers_cpp_test.cpp -----------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick 
9d89ec533Spatrick #include "memtag.h"
103cab2bb3Spatrick #include "tests/scudo_unit_test.h"
113cab2bb3Spatrick 
123cab2bb3Spatrick #include <atomic>
133cab2bb3Spatrick #include <condition_variable>
14*810390e3Srobert #include <fstream>
15d89ec533Spatrick #include <memory>
163cab2bb3Spatrick #include <mutex>
173cab2bb3Spatrick #include <thread>
183cab2bb3Spatrick #include <vector>
193cab2bb3Spatrick 
203cab2bb3Spatrick void operator delete(void *, size_t) noexcept;
213cab2bb3Spatrick void operator delete[](void *, size_t) noexcept;
223cab2bb3Spatrick 
233cab2bb3Spatrick // Note that every Cxx allocation function in the test binary will be fulfilled
243cab2bb3Spatrick // by Scudo. See the comment in the C counterpart of this file.
253cab2bb3Spatrick 
testCxxNew()263cab2bb3Spatrick template <typename T> static void testCxxNew() {
273cab2bb3Spatrick   T *P = new T;
283cab2bb3Spatrick   EXPECT_NE(P, nullptr);
293cab2bb3Spatrick   memset(P, 0x42, sizeof(T));
303cab2bb3Spatrick   EXPECT_DEATH(delete[] P, "");
313cab2bb3Spatrick   delete P;
323cab2bb3Spatrick   EXPECT_DEATH(delete P, "");
333cab2bb3Spatrick 
343cab2bb3Spatrick   P = new T;
353cab2bb3Spatrick   EXPECT_NE(P, nullptr);
363cab2bb3Spatrick   memset(P, 0x42, sizeof(T));
373cab2bb3Spatrick   operator delete(P, sizeof(T));
383cab2bb3Spatrick 
393cab2bb3Spatrick   P = new (std::nothrow) T;
403cab2bb3Spatrick   EXPECT_NE(P, nullptr);
413cab2bb3Spatrick   memset(P, 0x42, sizeof(T));
423cab2bb3Spatrick   delete P;
433cab2bb3Spatrick 
443cab2bb3Spatrick   const size_t N = 16U;
453cab2bb3Spatrick   T *A = new T[N];
463cab2bb3Spatrick   EXPECT_NE(A, nullptr);
473cab2bb3Spatrick   memset(A, 0x42, sizeof(T) * N);
483cab2bb3Spatrick   EXPECT_DEATH(delete A, "");
493cab2bb3Spatrick   delete[] A;
503cab2bb3Spatrick   EXPECT_DEATH(delete[] A, "");
513cab2bb3Spatrick 
523cab2bb3Spatrick   A = new T[N];
533cab2bb3Spatrick   EXPECT_NE(A, nullptr);
543cab2bb3Spatrick   memset(A, 0x42, sizeof(T) * N);
553cab2bb3Spatrick   operator delete[](A, sizeof(T) * N);
563cab2bb3Spatrick 
573cab2bb3Spatrick   A = new (std::nothrow) T[N];
583cab2bb3Spatrick   EXPECT_NE(A, nullptr);
593cab2bb3Spatrick   memset(A, 0x42, sizeof(T) * N);
603cab2bb3Spatrick   delete[] A;
613cab2bb3Spatrick }
623cab2bb3Spatrick 
633cab2bb3Spatrick class Pixel {
643cab2bb3Spatrick public:
653cab2bb3Spatrick   enum class Color { Red, Green, Blue };
663cab2bb3Spatrick   int X = 0;
673cab2bb3Spatrick   int Y = 0;
683cab2bb3Spatrick   Color C = Color::Red;
693cab2bb3Spatrick };
703cab2bb3Spatrick 
TEST(ScudoWrappersCppDeathTest,New)71d89ec533Spatrick TEST(ScudoWrappersCppDeathTest, New) {
72d89ec533Spatrick   if (getenv("SKIP_TYPE_MISMATCH")) {
73d89ec533Spatrick     printf("Skipped type mismatch tests.\n");
74d89ec533Spatrick     return;
75d89ec533Spatrick   }
763cab2bb3Spatrick   testCxxNew<bool>();
773cab2bb3Spatrick   testCxxNew<uint8_t>();
783cab2bb3Spatrick   testCxxNew<uint16_t>();
793cab2bb3Spatrick   testCxxNew<uint32_t>();
803cab2bb3Spatrick   testCxxNew<uint64_t>();
813cab2bb3Spatrick   testCxxNew<float>();
823cab2bb3Spatrick   testCxxNew<double>();
833cab2bb3Spatrick   testCxxNew<long double>();
843cab2bb3Spatrick   testCxxNew<Pixel>();
853cab2bb3Spatrick }
863cab2bb3Spatrick 
873cab2bb3Spatrick static std::mutex Mutex;
883cab2bb3Spatrick static std::condition_variable Cv;
89d89ec533Spatrick static bool Ready;
903cab2bb3Spatrick 
stressNew()913cab2bb3Spatrick static void stressNew() {
923cab2bb3Spatrick   std::vector<uintptr_t *> V;
933cab2bb3Spatrick   {
943cab2bb3Spatrick     std::unique_lock<std::mutex> Lock(Mutex);
953cab2bb3Spatrick     while (!Ready)
963cab2bb3Spatrick       Cv.wait(Lock);
973cab2bb3Spatrick   }
983cab2bb3Spatrick   for (size_t I = 0; I < 256U; I++) {
993cab2bb3Spatrick     const size_t N = std::rand() % 128U;
1003cab2bb3Spatrick     uintptr_t *P = new uintptr_t[N];
1013cab2bb3Spatrick     if (P) {
1023cab2bb3Spatrick       memset(P, 0x42, sizeof(uintptr_t) * N);
1033cab2bb3Spatrick       V.push_back(P);
1043cab2bb3Spatrick     }
1053cab2bb3Spatrick   }
1063cab2bb3Spatrick   while (!V.empty()) {
1073cab2bb3Spatrick     delete[] V.back();
1083cab2bb3Spatrick     V.pop_back();
1093cab2bb3Spatrick   }
1103cab2bb3Spatrick }
1113cab2bb3Spatrick 
TEST(ScudoWrappersCppTest,ThreadedNew)1123cab2bb3Spatrick TEST(ScudoWrappersCppTest, ThreadedNew) {
113d89ec533Spatrick   // TODO: Investigate why libc sometimes crashes with tag missmatch in
114d89ec533Spatrick   // __pthread_clockjoin_ex.
115d89ec533Spatrick   std::unique_ptr<scudo::ScopedDisableMemoryTagChecks> NoTags;
116d89ec533Spatrick   if (!SCUDO_ANDROID && scudo::archSupportsMemoryTagging() &&
117d89ec533Spatrick       scudo::systemSupportsMemoryTagging())
118d89ec533Spatrick     NoTags = std::make_unique<scudo::ScopedDisableMemoryTagChecks>();
119d89ec533Spatrick 
120d89ec533Spatrick   Ready = false;
1213cab2bb3Spatrick   std::thread Threads[32];
1223cab2bb3Spatrick   for (size_t I = 0U; I < sizeof(Threads) / sizeof(Threads[0]); I++)
1233cab2bb3Spatrick     Threads[I] = std::thread(stressNew);
1243cab2bb3Spatrick   {
1253cab2bb3Spatrick     std::unique_lock<std::mutex> Lock(Mutex);
1263cab2bb3Spatrick     Ready = true;
1273cab2bb3Spatrick     Cv.notify_all();
1283cab2bb3Spatrick   }
1293cab2bb3Spatrick   for (auto &T : Threads)
1303cab2bb3Spatrick     T.join();
1313cab2bb3Spatrick }
1323cab2bb3Spatrick 
1333cab2bb3Spatrick #if !SCUDO_FUCHSIA
TEST(ScudoWrappersCppTest,AllocAfterFork)1343cab2bb3Spatrick TEST(ScudoWrappersCppTest, AllocAfterFork) {
135*810390e3Srobert   // This test can fail flakily when ran as a part of large number of
136*810390e3Srobert   // other tests if the maxmimum number of mappings allowed is low.
137*810390e3Srobert   // We tried to reduce the number of iterations of the loops with
138*810390e3Srobert   // moderate success, so we will now skip this test under those
139*810390e3Srobert   // circumstances.
140*810390e3Srobert   if (SCUDO_LINUX) {
141*810390e3Srobert     long MaxMapCount = 0;
142*810390e3Srobert     // If the file can't be accessed, we proceed with the test.
143*810390e3Srobert     std::ifstream Stream("/proc/sys/vm/max_map_count");
144*810390e3Srobert     if (Stream.good()) {
145*810390e3Srobert       Stream >> MaxMapCount;
146*810390e3Srobert       if (MaxMapCount < 200000)
147*810390e3Srobert         return;
148*810390e3Srobert     }
149*810390e3Srobert   }
150*810390e3Srobert 
1513cab2bb3Spatrick   std::atomic_bool Stop;
1523cab2bb3Spatrick 
1533cab2bb3Spatrick   // Create threads that simply allocate and free different sizes.
1543cab2bb3Spatrick   std::vector<std::thread *> Threads;
1553cab2bb3Spatrick   for (size_t N = 0; N < 5; N++) {
1563cab2bb3Spatrick     std::thread *T = new std::thread([&Stop] {
1573cab2bb3Spatrick       while (!Stop) {
158*810390e3Srobert         for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) {
1593cab2bb3Spatrick           char *P = new char[1UL << SizeLog];
1603cab2bb3Spatrick           EXPECT_NE(P, nullptr);
1613cab2bb3Spatrick           // Make sure this value is not optimized away.
1623cab2bb3Spatrick           asm volatile("" : : "r,m"(P) : "memory");
1633cab2bb3Spatrick           delete[] P;
1643cab2bb3Spatrick         }
1653cab2bb3Spatrick       }
1663cab2bb3Spatrick     });
1673cab2bb3Spatrick     Threads.push_back(T);
1683cab2bb3Spatrick   }
1693cab2bb3Spatrick 
1703cab2bb3Spatrick   // Create a thread to fork and allocate.
171*810390e3Srobert   for (size_t N = 0; N < 50; N++) {
1723cab2bb3Spatrick     pid_t Pid;
1733cab2bb3Spatrick     if ((Pid = fork()) == 0) {
174*810390e3Srobert       for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) {
1753cab2bb3Spatrick         char *P = new char[1UL << SizeLog];
1763cab2bb3Spatrick         EXPECT_NE(P, nullptr);
1773cab2bb3Spatrick         // Make sure this value is not optimized away.
1783cab2bb3Spatrick         asm volatile("" : : "r,m"(P) : "memory");
1793cab2bb3Spatrick         // Make sure we can touch all of the allocation.
1803cab2bb3Spatrick         memset(P, 0x32, 1U << SizeLog);
1813cab2bb3Spatrick         // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr));
1823cab2bb3Spatrick         delete[] P;
1833cab2bb3Spatrick       }
1843cab2bb3Spatrick       _exit(10);
1853cab2bb3Spatrick     }
1863cab2bb3Spatrick     EXPECT_NE(-1, Pid);
1873cab2bb3Spatrick     int Status;
1883cab2bb3Spatrick     EXPECT_EQ(Pid, waitpid(Pid, &Status, 0));
1893cab2bb3Spatrick     EXPECT_FALSE(WIFSIGNALED(Status));
1903cab2bb3Spatrick     EXPECT_EQ(10, WEXITSTATUS(Status));
1913cab2bb3Spatrick   }
1923cab2bb3Spatrick 
1933cab2bb3Spatrick   printf("Waiting for threads to complete\n");
1943cab2bb3Spatrick   Stop = true;
1953cab2bb3Spatrick   for (auto Thread : Threads)
1963cab2bb3Spatrick     Thread->join();
1973cab2bb3Spatrick   Threads.clear();
1983cab2bb3Spatrick }
1993cab2bb3Spatrick #endif
200