274 lines · cpp
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-exception6//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// Android does not support checking for new/delete mismatches.21#if SCUDO_ANDROID22#define SKIP_MISMATCH_TESTS 123#else24#define SKIP_MISMATCH_TESTS 025#endif26 27void operator delete(void *, size_t) noexcept;28void operator delete[](void *, size_t) noexcept;29 30extern "C" {31#ifndef SCUDO_ENABLE_HOOKS_TESTS32#define SCUDO_ENABLE_HOOKS_TESTS 033#endif34 35#if (SCUDO_ENABLE_HOOKS_TESTS == 1) && (SCUDO_ENABLE_HOOKS == 0)36#error "Hooks tests should have hooks enabled as well!"37#endif38 39struct AllocContext {40 void *Ptr;41 size_t Size;42};43struct DeallocContext {44 void *Ptr;45};46static AllocContext AC;47static DeallocContext DC;48 49#if (SCUDO_ENABLE_HOOKS_TESTS == 1)50__attribute__((visibility("default"))) void __scudo_allocate_hook(void *Ptr,51 size_t Size) {52 AC.Ptr = Ptr;53 AC.Size = Size;54}55__attribute__((visibility("default"))) void __scudo_deallocate_hook(void *Ptr) {56 DC.Ptr = Ptr;57}58#endif // (SCUDO_ENABLE_HOOKS_TESTS == 1)59}60 61class ScudoWrappersCppTest : public Test {62protected:63 void SetUp() override {64 if (SCUDO_ENABLE_HOOKS && !SCUDO_ENABLE_HOOKS_TESTS)65 printf("Hooks are enabled but hooks tests are disabled.\n");66 }67 68 void verifyAllocHookPtr(UNUSED void *Ptr) {69 if (SCUDO_ENABLE_HOOKS_TESTS)70 EXPECT_EQ(Ptr, AC.Ptr);71 }72 void verifyAllocHookSize(UNUSED size_t Size) {73 if (SCUDO_ENABLE_HOOKS_TESTS)74 EXPECT_EQ(Size, AC.Size);75 }76 void verifyDeallocHookPtr(UNUSED void *Ptr) {77 if (SCUDO_ENABLE_HOOKS_TESTS)78 EXPECT_EQ(Ptr, DC.Ptr);79 }80 81 template <typename T> void testCxxNew() {82 T *P = new T;83 EXPECT_NE(P, nullptr);84 verifyAllocHookPtr(P);85 verifyAllocHookSize(sizeof(T));86 memset(P, 0x42, sizeof(T));87 EXPECT_DEATH(delete[] P, "");88 delete P;89 verifyDeallocHookPtr(P);90 EXPECT_DEATH(delete P, "");91 92 P = new T;93 EXPECT_NE(P, nullptr);94 memset(P, 0x42, sizeof(T));95 operator delete(P, sizeof(T));96 verifyDeallocHookPtr(P);97 98 P = new (std::nothrow) T;99 verifyAllocHookPtr(P);100 verifyAllocHookSize(sizeof(T));101 EXPECT_NE(P, nullptr);102 memset(P, 0x42, sizeof(T));103 delete P;104 verifyDeallocHookPtr(P);105 106 const size_t N = 16U;107 T *A = new T[N];108 EXPECT_NE(A, nullptr);109 verifyAllocHookPtr(A);110 verifyAllocHookSize(sizeof(T) * N);111 memset(A, 0x42, sizeof(T) * N);112 EXPECT_DEATH(delete A, "");113 delete[] A;114 verifyDeallocHookPtr(A);115 EXPECT_DEATH(delete[] A, "");116 117 A = new T[N];118 EXPECT_NE(A, nullptr);119 memset(A, 0x42, sizeof(T) * N);120 operator delete[](A, sizeof(T) * N);121 verifyDeallocHookPtr(A);122 123 A = new (std::nothrow) T[N];124 verifyAllocHookPtr(A);125 verifyAllocHookSize(sizeof(T) * N);126 EXPECT_NE(A, nullptr);127 memset(A, 0x42, sizeof(T) * N);128 delete[] A;129 verifyDeallocHookPtr(A);130 }131};132using ScudoWrappersCppDeathTest = ScudoWrappersCppTest;133 134class Pixel {135public:136 enum class Color { Red, Green, Blue };137 int X = 0;138 int Y = 0;139 Color C = Color::Red;140};141 142// Note that every Cxx allocation function in the test binary will be fulfilled143// by Scudo. See the comment in the C counterpart of this file.144 145TEST_F(ScudoWrappersCppDeathTest, New) {146 if (getenv("SKIP_TYPE_MISMATCH") || SKIP_MISMATCH_TESTS) {147 printf("Skipped type mismatch tests.\n");148 return;149 }150 testCxxNew<bool>();151 testCxxNew<uint8_t>();152 testCxxNew<uint16_t>();153 testCxxNew<uint32_t>();154 testCxxNew<uint64_t>();155 testCxxNew<float>();156 testCxxNew<double>();157 testCxxNew<long double>();158 testCxxNew<Pixel>();159}160 161static std::mutex Mutex;162static std::condition_variable Cv;163static bool Ready;164 165static void stressNew() {166 std::vector<uintptr_t *> V;167 {168 std::unique_lock<std::mutex> Lock(Mutex);169 while (!Ready)170 Cv.wait(Lock);171 }172 for (size_t I = 0; I < 256U; I++) {173 const size_t N = static_cast<size_t>(std::rand()) % 128U;174 uintptr_t *P = new uintptr_t[N];175 if (P) {176 memset(P, 0x42, sizeof(uintptr_t) * N);177 V.push_back(P);178 }179 }180 while (!V.empty()) {181 delete[] V.back();182 V.pop_back();183 }184}185 186TEST_F(ScudoWrappersCppTest, ThreadedNew) {187 // TODO: Investigate why libc sometimes crashes with tag missmatch in188 // __pthread_clockjoin_ex.189 std::unique_ptr<scudo::ScopedDisableMemoryTagChecks> NoTags;190 if (!SCUDO_ANDROID && scudo::archSupportsMemoryTagging() &&191 scudo::systemSupportsMemoryTagging())192 NoTags = std::make_unique<scudo::ScopedDisableMemoryTagChecks>();193 194 Ready = false;195 std::thread Threads[32];196 for (size_t I = 0U; I < sizeof(Threads) / sizeof(Threads[0]); I++)197 Threads[I] = std::thread(stressNew);198 {199 std::unique_lock<std::mutex> Lock(Mutex);200 Ready = true;201 Cv.notify_all();202 }203 for (auto &T : Threads)204 T.join();205}206 207#if !SCUDO_FUCHSIA208TEST_F(ScudoWrappersCppTest, AllocAfterFork) {209 // This test can fail flakily when ran as a part of large number of210 // other tests if the maxmimum number of mappings allowed is low.211 // We tried to reduce the number of iterations of the loops with212 // moderate success, so we will now skip this test under those213 // circumstances.214 if (SCUDO_LINUX) {215 long MaxMapCount = 0;216 // If the file can't be accessed, we proceed with the test.217 std::ifstream Stream("/proc/sys/vm/max_map_count");218 if (Stream.good()) {219 Stream >> MaxMapCount;220 if (MaxMapCount < 200000)221 return;222 }223 }224 225 std::atomic_bool Stop;226 227 // Create threads that simply allocate and free different sizes.228 std::vector<std::thread *> Threads;229 for (size_t N = 0; N < 5; N++) {230 std::thread *T = new std::thread([&Stop] {231 while (!Stop) {232 for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) {233 char *P = new char[1UL << SizeLog];234 EXPECT_NE(P, nullptr);235 // Make sure this value is not optimized away.236 asm volatile("" : : "r,m"(P) : "memory");237 delete[] P;238 }239 }240 });241 Threads.push_back(T);242 }243 244 // Create a thread to fork and allocate.245 for (size_t N = 0; N < 50; N++) {246 pid_t Pid;247 if ((Pid = fork()) == 0) {248 for (size_t SizeLog = 3; SizeLog <= 20; SizeLog++) {249 char *P = new char[1UL << SizeLog];250 EXPECT_NE(P, nullptr);251 // Make sure this value is not optimized away.252 asm volatile("" : : "r,m"(P) : "memory");253 // Make sure we can touch all of the allocation.254 memset(P, 0x32, 1U << SizeLog);255 // EXPECT_LE(1U << SizeLog, malloc_usable_size(ptr));256 delete[] P;257 }258 _exit(10);259 }260 EXPECT_NE(-1, Pid);261 int Status;262 EXPECT_EQ(Pid, waitpid(Pid, &Status, 0));263 EXPECT_FALSE(WIFSIGNALED(Status));264 EXPECT_EQ(10, WEXITSTATUS(Status));265 }266 267 printf("Waiting for threads to complete\n");268 Stop = true;269 for (auto Thread : Threads)270 Thread->join();271 Threads.clear();272}273#endif274