brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.2 KiB · 2c14ff5 Raw
195 lines · cpp
1//===-- recoverable.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 <atomic>10#include <mutex>11#include <regex>12#include <string>13#include <thread>14#include <vector>15 16#include "gwp_asan/common.h"17#include "gwp_asan/crash_handler.h"18#include "gwp_asan/tests/harness.h"19 20TEST_P(BacktraceGuardedPoolAllocator, MultipleDoubleFreeOnlyOneOutput) {21  SCOPED_TRACE("");22  void *Ptr = AllocateMemory(GPA);23  DeallocateMemory(GPA, Ptr);24  // First time should generate a crash report.25  DeallocateMemory(GPA, Ptr);26  CheckOnlyOneGwpAsanCrash(GetOutputBuffer());27  ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free"));28 29  // Ensure the crash is only reported once.30  GetOutputBuffer().clear();31  for (size_t i = 0; i < 100; ++i) {32    DeallocateMemory(GPA, Ptr);33    ASSERT_TRUE(GetOutputBuffer().empty());34  }35}36 37TEST_P(BacktraceGuardedPoolAllocator, MultipleInvalidFreeOnlyOneOutput) {38  SCOPED_TRACE("");39  char *Ptr = static_cast<char *>(AllocateMemory(GPA));40  // First time should generate a crash report.41  DeallocateMemory(GPA, Ptr + 1);42  CheckOnlyOneGwpAsanCrash(GetOutputBuffer());43  ASSERT_NE(std::string::npos, GetOutputBuffer().find("Invalid (Wild) Free"));44 45  // Ensure the crash is only reported once.46  GetOutputBuffer().clear();47  for (size_t i = 0; i < 100; ++i) {48    DeallocateMemory(GPA, Ptr + 1);49    ASSERT_TRUE(GetOutputBuffer().empty());50  }51}52 53TEST_P(BacktraceGuardedPoolAllocator, MultipleUseAfterFreeOnlyOneOutput) {54  SCOPED_TRACE("");55  void *Ptr = AllocateMemory(GPA);56  DeallocateMemory(GPA, Ptr);57  // First time should generate a crash report.58  TouchMemory(Ptr);59  ASSERT_NE(std::string::npos, GetOutputBuffer().find("Use After Free"));60 61  // Ensure the crash is only reported once.62  GetOutputBuffer().clear();63  for (size_t i = 0; i < 100; ++i) {64    TouchMemory(Ptr);65    ASSERT_TRUE(GetOutputBuffer().empty());66  }67}68 69TEST_P(BacktraceGuardedPoolAllocator, MultipleBufferOverflowOnlyOneOutput) {70  SCOPED_TRACE("");71  char *Ptr = static_cast<char *>(AllocateMemory(GPA));72  // First time should generate a crash report.73  TouchMemory(Ptr - 16);74  TouchMemory(Ptr + 16);75  CheckOnlyOneGwpAsanCrash(GetOutputBuffer());76  if (GetOutputBuffer().find("Buffer Overflow") == std::string::npos &&77      GetOutputBuffer().find("Buffer Underflow") == std::string::npos)78    FAIL() << "Failed to detect buffer underflow/overflow:\n"79           << GetOutputBuffer();80 81  // Ensure the crash is only reported once.82  GetOutputBuffer().clear();83  for (size_t i = 0; i < 100; ++i) {84    TouchMemory(Ptr - 16);85    TouchMemory(Ptr + 16);86    ASSERT_TRUE(GetOutputBuffer().empty()) << GetOutputBuffer();87  }88}89 90TEST_P(BacktraceGuardedPoolAllocator, OneDoubleFreeOneUseAfterFree) {91  SCOPED_TRACE("");92  void *Ptr = AllocateMemory(GPA);93  DeallocateMemory(GPA, Ptr);94  // First time should generate a crash report.95  DeallocateMemory(GPA, Ptr);96  CheckOnlyOneGwpAsanCrash(GetOutputBuffer());97  ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free"));98 99  // Ensure the crash is only reported once.100  GetOutputBuffer().clear();101  for (size_t i = 0; i < 100; ++i) {102    DeallocateMemory(GPA, Ptr);103    ASSERT_TRUE(GetOutputBuffer().empty());104  }105}106 107// We use double-free to detect that each slot can generate as single error.108// Use-after-free would also be acceptable, but buffer-overflow wouldn't be, as109// the random left/right alignment means that one right-overflow can disable110// page protections, and a subsequent left-overflow of a slot that's on the111// right hand side may not trap.112TEST_P(BacktraceGuardedPoolAllocator, OneErrorReportPerSlot) {113  SCOPED_TRACE("");114  std::vector<void *> Ptrs;115  for (size_t i = 0; i < GPA.getAllocatorState()->MaxSimultaneousAllocations;116       ++i) {117    void *Ptr = AllocateMemory(GPA);118    ASSERT_NE(Ptr, nullptr);119    Ptrs.push_back(Ptr);120    DeallocateMemory(GPA, Ptr);121    DeallocateMemory(GPA, Ptr);122    CheckOnlyOneGwpAsanCrash(GetOutputBuffer());123    ASSERT_NE(std::string::npos, GetOutputBuffer().find("Double Free"));124    // Ensure the crash from this slot is only reported once.125    GetOutputBuffer().clear();126    DeallocateMemory(GPA, Ptr);127    ASSERT_TRUE(GetOutputBuffer().empty());128    // Reset the buffer, as we're gonna move to the next allocation.129    GetOutputBuffer().clear();130  }131 132  // All slots should have been used. No further errors should occur.133  for (size_t i = 0; i < 100; ++i)134    ASSERT_EQ(AllocateMemory(GPA), nullptr);135  for (void *Ptr : Ptrs) {136    DeallocateMemory(GPA, Ptr);137    TouchMemory(Ptr);138  }139  ASSERT_TRUE(GetOutputBuffer().empty());140}141 142void singleAllocThrashTask(gwp_asan::GuardedPoolAllocator *GPA,143                           std::atomic<bool> *StartingGun,144                           unsigned NumIterations, unsigned Job, char *Ptr) {145  while (!*StartingGun) {146    // Wait for starting gun.147  }148 149  for (unsigned i = 0; i < NumIterations; ++i) {150    switch (Job) {151    case 0:152      DeallocateMemory(*GPA, Ptr);153      break;154    case 1:155      DeallocateMemory(*GPA, Ptr + 1);156      break;157    case 2:158      TouchMemory(Ptr);159      break;160    case 3:161      TouchMemory(Ptr - 16);162      TouchMemory(Ptr + 16);163      break;164    default:165      __builtin_trap();166    }167  }168}169 170void runInterThreadThrashingSingleAlloc(unsigned NumIterations,171                                        gwp_asan::GuardedPoolAllocator *GPA) {172  std::atomic<bool> StartingGun{false};173  std::vector<std::thread> Threads;174  constexpr unsigned kNumThreads = 4;175 176  char *Ptr = static_cast<char *>(AllocateMemory(*GPA));177 178  for (unsigned i = 0; i < kNumThreads; ++i) {179    Threads.emplace_back(singleAllocThrashTask, GPA, &StartingGun,180                         NumIterations, i, Ptr);181  }182 183  StartingGun = true;184 185  for (auto &T : Threads)186    T.join();187}188 189TEST_P(BacktraceGuardedPoolAllocator, InterThreadThrashingSingleAlloc) {190  SCOPED_TRACE("");191  constexpr unsigned kNumIterations = 100000;192  runInterThreadThrashingSingleAlloc(kNumIterations, &GPA);193  CheckOnlyOneGwpAsanCrash(GetOutputBuffer());194}195