brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.2 KiB · c802ed2 Raw
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