[compiler-rt] r362636 - [GWP-ASan] Core Guarded Pool Allocator [4].

Mitch Phillips via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 5 20:11:37 PDT 2019


Never mind, looks like c++11 is part of your build line. Let me take a
further look.

On Wed, Jun 5, 2019 at 8:10 PM Mitch Phillips <mitchphillips at outlook.com>
wrote:

> Hi Douglas,
>
> It looks like the default in gcc 5.4 is non-c++11 (
> https://godbolt.org/z/gBZ2O4).
>
> I can't get to a machine that can commit right now - I can submit a patch
> that adds "-std=c++11" to GWP_ASAN_CFLAGS (compiler-rt/lib/gwp_asan/CMakeLists.txt:18)
> tomorrow, or if you don't mind submitting the oneliner that should also
> work :). Alternatively, you can change your DCMAKE_CXX_FLAGS="-std=c++11"
> to work around the problem until tomorrow.
>
> Thanks,
> Mitch.
>
> On Wed, Jun 5, 2019 at 7:24 PM <douglas.yung at sony.com> wrote:
>
>> Hi Mitch,
>>
>> Your commit is failing to build on our internal linux gcc 5.4.0 buildbot.
>> Can you take a look?
>>
>> [2786/4815] Building CXX object
>> tools/llvm-diff/CMakeFiles/llvm-diff.dir/llvm-diff.cpp.o
>> FAILED: CCACHE_CPP2=yes CCACHE_HASHDIR=yes /usr/bin/ccache
>> /usr/lib/ccache/g++   -D_DEBUG -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS
>> -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
>> -Iprojects/compiler-rt/lib/gwp_asan
>> -I/home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan
>> -Iinclude
>> -I/home/siadmin/jenkins/w/opensource/opensource_build/llvm/include
>> -I/home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/..
>> -fPIC -fvisibility-inlines-hidden -Werror=date-time -std=c++11 -Wall
>> -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual
>> -Wno-missing-field-initializers -pedantic -Wno-long-long
>> -Wno-maybe-uninitialized -Wdelete-non-virtual-dtor -Wno-comment
>> -fdiagnostics-color -ffunction-sections -fdata-sections -Wall -std=c++11
>> -Wno-unused-parameter -O3    -UNDEBUG  -m64 -fno-lto -fno-rtti
>> -fno-exceptions -nostdinc++ -pthread -MMD -MT
>> projects/compiler-rt/lib/gwp_asan/CMakeFiles/clang_rt.gwp_asan-x86_64.dir/guarded_pool_allocator.cpp.o
>> -MF
>> projects/compiler-rt/lib/gwp_asan/CMakeFiles/clang_rt.gwp_asan-x86_64.dir/guarded_pool_allocator.cpp.o.d
>> -o
>> projects/compiler-rt/lib/gwp_asan/CMakeFiles/clang_rt.gwp_asan-x86_64.dir/guarded_pool_allocator.cpp.o
>> -c
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:
>> In function 'bool gwp_asan::printErrorType(Error, uintptr_t,
>> AllocationMetadata*, gwp_asan::options::Printf_t)':
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:317:8:
>> error: 'Error' is not a class, namespace, or enumeration
>>    case Error::UNKNOWN:
>>         ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:323:8:
>> error: 'Error' is not a class, namespace, or enumeration
>>    case Error::USE_AFTER_FREE:
>>         ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:327:8:
>> error: 'Error' is not a class, namespace, or enumeration
>>    case Error::DOUBLE_FREE:
>>         ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:331:8:
>> error: 'Error' is not a class, namespace, or enumeration
>>    case Error::INVALID_FREE:
>>         ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:340:8:
>> error: 'Error' is not a class, namespace, or enumeration
>>    case Error::BUFFER_OVERFLOW:
>>         ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:344:8:
>> error: 'Error' is not a class, namespace, or enumeration
>>    case Error::BUFFER_UNDERFLOW:
>>         ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:316:10:
>> warning: enumeration value 'UNKNOWN' not handled in switch [-Wswitch]
>>    switch (Error) {
>>           ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:316:10:
>> warning: enumeration value 'USE_AFTER_FREE' not handled in switch [-Wswitch]
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:316:10:
>> warning: enumeration value 'DOUBLE_FREE' not handled in switch [-Wswitch]
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:316:10:
>> warning: enumeration value 'INVALID_FREE' not handled in switch [-Wswitch]
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:316:10:
>> warning: enumeration value 'BUFFER_OVERFLOW' not handled in switch
>> [-Wswitch]
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:316:10:
>> warning: enumeration value 'BUFFER_UNDERFLOW' not handled in switch
>> [-Wswitch]
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:
>> In function 'void gwp_asan::printThreadInformation(Error, uintptr_t,
>> AllocationMetadata*, gwp_asan::options::Printf_t)':
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:373:16:
>> error: 'Error' is not a class, namespace, or enumeration
>>    if (Error == Error::USE_AFTER_FREE || Error == Error::DOUBLE_FREE) {
>>                 ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:373:50:
>> error: 'Error' is not a class, namespace, or enumeration
>>    if (Error == Error::USE_AFTER_FREE || Error == Error::DOUBLE_FREE) {
>>                                                   ^
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:
>> In member function 'void
>> gwp_asan::GuardedPoolAllocator::reportErrorInternal(uintptr_t,
>> gwp_asan::GuardedPoolAllocator::Error)':
>> /home/siadmin/jenkins/w/opensource/opensource_build/llvm/projects/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp:404:16:
>> error: 'Error' is not a class, namespace, or enumeration
>>    if (Error == Error::UNKNOWN) {
>>                 ^
>>
>> Douglas Yung
>>
>> -----Original Message-----
>> From: llvm-commits <llvm-commits-bounces at lists.llvm.org> On Behalf Of
>> Mitch Phillips via llvm-commits
>> Sent: Wednesday, June 5, 2019 12:43
>> To: llvm-commits at lists.llvm.org
>> Subject: [compiler-rt] r362636 - [GWP-ASan] Core Guarded Pool Allocator
>> [4].
>>
>> Author: hctim
>> Date: Wed Jun  5 12:42:48 2019
>> New Revision: 362636
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=362636&view=rev
>> Log:
>> [GWP-ASan] Core Guarded Pool Allocator [4].
>>
>> Summary:
>> See D60593 for further information.
>>
>> This patch introduces the core of GWP-ASan, being the guarded pool
>> allocator. This class contains the logic for creating and maintaining
>> allocations in the guarded pool. Its public interface is to be utilised by
>> supporting allocators in order to provide sampled guarded allocation
>> behaviour.
>>
>> This patch also contains basic functionality tests of the allocator as
>> unittests. The error-catching behaviour will be tested in upcoming patches
>> that use Scudo as an implementing allocator.
>>
>> Reviewers: vlad.tsyrklevich, eugenis, jfb
>>
>> Reviewed By: vlad.tsyrklevich
>>
>> Subscribers: dexonsmith, kubamracek, mgorny, cryptoad, jfb, #sanitizers,
>> llvm-commits, morehouse
>>
>> Tags: #sanitizers, #llvm
>>
>> Differential Revision: https://reviews.llvm.org/D62872
>>
>> Added:
>>     compiler-rt/trunk/lib/gwp_asan/definitions.h
>>     compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.cpp
>>     compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.h
>>
>> compiler-rt/trunk/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
>>     compiler-rt/trunk/lib/gwp_asan/tests/alignment.cpp
>>     compiler-rt/trunk/lib/gwp_asan/tests/basic.cpp
>>     compiler-rt/trunk/lib/gwp_asan/tests/harness.h
>>     compiler-rt/trunk/lib/gwp_asan/tests/slot_reuse.cpp
>>     compiler-rt/trunk/lib/gwp_asan/tests/thread_contention.cpp
>> Modified:
>>     compiler-rt/trunk/lib/gwp_asan/CMakeLists.txt
>>     compiler-rt/trunk/lib/gwp_asan/tests/CMakeLists.txt
>>
>> Modified: compiler-rt/trunk/lib/gwp_asan/CMakeLists.txt
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/CMakeLists.txt?rev=362636&r1=362635&r2=362636&view=diff
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/CMakeLists.txt (original)
>> +++ compiler-rt/trunk/lib/gwp_asan/CMakeLists.txt Wed Jun  5 12:42:48
>> +++ 2019
>> @@ -3,15 +3,19 @@ add_compiler_rt_component(gwp_asan)
>>  include_directories(..)
>>
>>  set(GWP_ASAN_SOURCES
>> +  platform_specific/guarded_pool_allocator_posix.cpp
>>    platform_specific/mutex_posix.cpp
>> +  guarded_pool_allocator.cpp
>>    random.cpp
>>  )
>>
>>  set(GWP_ASAN_HEADERS
>> +  definitions.h
>> +  guarded_pool_allocator.h
>>    mutex.h
>> -  random.h
>>    options.h
>>    options.inc
>> +  random.h
>>  )
>>
>>  # Ensure that GWP-ASan meets the delegated requirements of some
>> supporting
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/definitions.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/definitions.h?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/definitions.h (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/definitions.h Wed Jun  5 12:42:48
>> +++ 2019
>> @@ -0,0 +1,29 @@
>> +//===-- gwp_asan_definitions.h ----------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#ifndef GWP_ASAN_DEFINITIONS_H_
>> +#define GWP_ASAN_DEFINITIONS_H_
>> +
>> +#define TLS_INITIAL_EXEC __thread
>> +__attribute__((tls_model("initial-exec")))
>> +
>> +#ifdef LIKELY
>> +# undef LIKELY
>> +#endif // defined(LIKELY)
>> +#define LIKELY(X) __builtin_expect(!!(X), 1)
>> +
>> +#ifdef UNLIKELY
>> +# undef UNLIKELY
>> +#endif // defined(UNLIKELY)
>> +#define UNLIKELY(X) __builtin_expect(!!(X), 0)
>> +
>> +#ifdef ALWAYS_INLINE
>> +# undef ALWAYS_INLINE
>> +#endif // defined(ALWAYS_INLINE)
>> +#define ALWAYS_INLINE inline __attribute__((always_inline))
>> +
>> +#endif // GWP_ASAN_DEFINITIONS_H_
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.cpp?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.cpp (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.cpp Wed Jun  5
>> +++ 12:42:48 2019
>> @@ -0,0 +1,433 @@
>> +//===-- guarded_pool_allocator.cpp ------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#include "gwp_asan/guarded_pool_allocator.h"
>> +
>> +#include "gwp_asan/options.h"
>> +
>> +#include <assert.h>
>> +#include <stdlib.h>
>> +#include <string.h>
>> +#include <time.h>
>> +
>> +using AllocationMetadata =
>> +gwp_asan::GuardedPoolAllocator::AllocationMetadata;
>> +using Error = gwp_asan::GuardedPoolAllocator::Error;
>> +
>> +namespace gwp_asan {
>> +namespace {
>> +// Forward declare the pointer to the singleton version of this class.
>> +// Instantiated during initialisation, this allows the signal handler
>> +// to find this class in order to deduce the root cause of failures.
>> +Must not be // referenced by users outside this translation unit, in
>> +order to avoid // init-order-fiasco.
>> +GuardedPoolAllocator *SingletonPtr = nullptr; } // anonymous namespace
>> +
>> +// Gets the singleton implementation of this class. Thread-compatible
>> +until // init() is called, thread-safe afterwards.
>> +GuardedPoolAllocator *getSingleton() { return SingletonPtr; }
>> +
>> +void GuardedPoolAllocator::AllocationMetadata::RecordAllocation(
>> +    uintptr_t AllocAddr, size_t AllocSize) {
>> +  Addr = AllocAddr;
>> +  Size = AllocSize;
>> +  IsDeallocated = false;
>> +
>> +  // TODO(hctim): Implement stack trace collection.
>> +  // TODO(hctim): Ask the caller to provide the thread ID, so we don't
>> +waste
>> +  // other thread's time getting the thread ID under lock.
>> +  AllocationTrace.ThreadID = getThreadID();
>> +  DeallocationTrace.ThreadID = kInvalidThreadID;
>> +  AllocationTrace.Trace[0] = 0;
>> +  DeallocationTrace.Trace[0] = 0;
>> +}
>> +
>> +void GuardedPoolAllocator::AllocationMetadata::RecordDeallocation() {
>> +  IsDeallocated = true;
>> +  // TODO(hctim): Implement stack trace collection.
>> +  DeallocationTrace.ThreadID = getThreadID(); }
>> +
>> +void GuardedPoolAllocator::init(const options::Options &Opts) {
>> +  // Note: We return from the constructor here if GWP-ASan is not
>> available.
>> +  // This will stop heap-allocation of class members, as well as mmap()
>> +of the
>> +  // guarded slots.
>> +  if (!Opts.Enabled || Opts.SampleRate == 0 ||
>> +      Opts.MaxSimultaneousAllocations == 0)
>> +    return;
>> +
>> +  // TODO(hctim): Add a death unit test for this.
>> +  if (SingletonPtr) {
>> +    (*SingletonPtr->Printf)(
>> +        "GWP-ASan Error: init() has already been called.\n");
>> +    exit(EXIT_FAILURE);
>> +  }
>> +
>> +  if (Opts.SampleRate < 0) {
>> +    Opts.Printf("GWP-ASan Error: SampleRate is < 0.\n");
>> +    exit(EXIT_FAILURE);
>> +  }
>> +
>> +  if (Opts.SampleRate > INT32_MAX) {
>> +    Opts.Printf("GWP-ASan Error: SampleRate is > 2^31.\n");
>> +    exit(EXIT_FAILURE);
>> +  }
>> +
>> +  if (Opts.MaxSimultaneousAllocations < 0) {
>> +    Opts.Printf("GWP-ASan Error: MaxSimultaneousAllocations is < 0.\n");
>> +    exit(EXIT_FAILURE);
>> +  }
>> +
>> +  SingletonPtr = this;
>> +
>> +  MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
>> +
>> +  PageSize = getPlatformPageSize();
>> +
>> +  PerfectlyRightAlign = Opts.PerfectlyRightAlign;  Printf =
>> + Opts.Printf;
>> +
>> +  size_t PoolBytesRequired =
>> +      PageSize * (1 + MaxSimultaneousAllocations) +
>> +      MaxSimultaneousAllocations * maximumAllocationSize();  void
>> + *GuardedPoolMemory = mapMemory(PoolBytesRequired);
>> +
>> +  size_t BytesRequired = MaxSimultaneousAllocations *
>> + sizeof(*Metadata);  Metadata = reinterpret_cast<AllocationMetadata
>> + *>(mapMemory(BytesRequired));  markReadWrite(Metadata, BytesRequired);
>> +
>> +  // Allocate memory and set up the free pages queue.
>> +  BytesRequired = MaxSimultaneousAllocations * sizeof(*FreeSlots);
>> + FreeSlots = reinterpret_cast<size_t *>(mapMemory(BytesRequired));
>> + markReadWrite(FreeSlots, BytesRequired);
>> +
>> +  // Multiply the sample rate by 2 to give a good, fast approximation
>> + for (1 /  // SampleRate) chance of sampling.
>> +  if (Opts.SampleRate != 1)
>> +    AdjustedSampleRate = static_cast<uint32_t>(Opts.SampleRate) * 2;
>> + else
>> +    AdjustedSampleRate = 1;
>> +
>> +  GuardedPagePool = reinterpret_cast<uintptr_t>(GuardedPoolMemory);
>> +  GuardedPagePoolEnd =
>> +      reinterpret_cast<uintptr_t>(GuardedPoolMemory) +
>> + PoolBytesRequired;
>> +
>> +  // Ensure that signal handlers are installed as late as possible, as
>> +the class
>> +  // is not thread-safe until init() is finished, and thus a SIGSEGV
>> +may cause a
>> +  // race to members if recieved during init().
>> +  if (Opts.InstallSignalHandlers)
>> +    installSignalHandlers();
>> +}
>> +
>> +void *GuardedPoolAllocator::allocate(size_t Size) {
>> +  if (Size == 0 || Size > maximumAllocationSize())
>> +    return nullptr;
>> +
>> +  size_t Index;
>> +  {
>> +    ScopedLock L(PoolMutex);
>> +    Index = reserveSlot();
>> +  }
>> +
>> +  if (Index == kInvalidSlotID)
>> +    return nullptr;
>> +
>> +  uintptr_t Ptr = slotToAddr(Index);
>> +  Ptr += allocationSlotOffset(Size);
>> +  AllocationMetadata *Meta = addrToMetadata(Ptr);
>> +
>> +  // If a slot is multiple pages in size, and the allocation takes up a
>> + single  // page, we can improve overflow detection by leaving the
>> + unused pages as  // unmapped.
>> +  markReadWrite(reinterpret_cast<void *>(getPageAddr(Ptr)), Size);
>> +
>> +  Meta->RecordAllocation(Ptr, Size);
>> +
>> +  return reinterpret_cast<void *>(Ptr); }
>> +
>> +void GuardedPoolAllocator::deallocate(void *Ptr) {
>> +  assert(pointerIsMine(Ptr) && "Pointer is not mine!");
>> +  uintptr_t UPtr = reinterpret_cast<uintptr_t>(Ptr);
>> +  uintptr_t SlotStart = slotToAddr(addrToSlot(UPtr));
>> +  AllocationMetadata *Meta = addrToMetadata(UPtr);
>> +  if (Meta->Addr != UPtr) {
>> +    reportError(UPtr, Error::INVALID_FREE);
>> +    exit(EXIT_FAILURE);
>> +  }
>> +
>> +  // Intentionally scope the mutex here, so that other threads can
>> + access the  // pool during the expensive markInaccessible() call.
>> +  {
>> +    ScopedLock L(PoolMutex);
>> +    if (Meta->IsDeallocated) {
>> +      reportError(UPtr, Error::DOUBLE_FREE);
>> +      exit(EXIT_FAILURE);
>> +    }
>> +
>> +    // Ensure that the deallocation is recorded before marking the page
>> as
>> +    // inaccessible. Otherwise, a racy use-after-free will have
>> inconsistent
>> +    // metadata.
>> +    Meta->RecordDeallocation();
>> +  }
>> +
>> +  markInaccessible(reinterpret_cast<void *>(SlotStart),
>> +                   maximumAllocationSize());
>> +
>> +  // And finally, lock again to release the slot back into the pool.
>> +  ScopedLock L(PoolMutex);
>> +  freeSlot(addrToSlot(UPtr));
>> +}
>> +
>> +size_t GuardedPoolAllocator::getSize(const void *Ptr) {
>> +  assert(pointerIsMine(Ptr));
>> +  ScopedLock L(PoolMutex);
>> +  AllocationMetadata *Meta =
>> +addrToMetadata(reinterpret_cast<uintptr_t>(Ptr));
>> +  assert(Meta->Addr == reinterpret_cast<uintptr_t>(Ptr));
>> +  return Meta->Size;
>> +}
>> +
>> +size_t GuardedPoolAllocator::maximumAllocationSize() const { return
>> +PageSize; }
>> +
>> +AllocationMetadata *GuardedPoolAllocator::addrToMetadata(uintptr_t Ptr)
>> +const {
>> +  return &Metadata[addrToSlot(Ptr)];
>> +}
>> +
>> +size_t GuardedPoolAllocator::addrToSlot(uintptr_t Ptr) const {
>> +  assert(pointerIsMine(reinterpret_cast<void *>(Ptr)));
>> +  size_t ByteOffsetFromPoolStart = Ptr - GuardedPagePool;
>> +  return ByteOffsetFromPoolStart / (maximumAllocationSize() +
>> +PageSize); }
>> +
>> +uintptr_t GuardedPoolAllocator::slotToAddr(size_t N) const {
>> +  return GuardedPagePool + (PageSize * (1 + N)) +
>> +(maximumAllocationSize() * N); }
>> +
>> +uintptr_t GuardedPoolAllocator::getPageAddr(uintptr_t Ptr) const {
>> +  assert(pointerIsMine(reinterpret_cast<void *>(Ptr)));
>> +  return Ptr & ~(static_cast<uintptr_t>(PageSize) - 1); }
>> +
>> +bool GuardedPoolAllocator::isGuardPage(uintptr_t Ptr) const {
>> +  assert(pointerIsMine(reinterpret_cast<void *>(Ptr)));
>> +  size_t PageOffsetFromPoolStart = (Ptr - GuardedPagePool) / PageSize;
>> +  size_t PagesPerSlot = maximumAllocationSize() / PageSize;
>> +  return (PageOffsetFromPoolStart % (PagesPerSlot + 1)) == 0; }
>> +
>> +size_t GuardedPoolAllocator::reserveSlot() {
>> +  // Avoid potential reuse of a slot before we have made at least a
>> +single
>> +  // allocation in each slot. Helps with our use-after-free detection.
>> +  if (NumSampledAllocations < MaxSimultaneousAllocations)
>> +    return NumSampledAllocations++;
>> +
>> +  if (FreeSlotsLength == 0)
>> +    return kInvalidSlotID;
>> +
>> +  size_t ReservedIndex = getRandomUnsigned32() % FreeSlotsLength;
>> +  size_t SlotIndex = FreeSlots[ReservedIndex];
>> +  FreeSlots[ReservedIndex] = FreeSlots[--FreeSlotsLength];
>> +  return SlotIndex;
>> +}
>> +
>> +void GuardedPoolAllocator::freeSlot(size_t SlotIndex) {
>> +  assert(FreeSlotsLength < MaxSimultaneousAllocations);
>> +  FreeSlots[FreeSlotsLength++] = SlotIndex; }
>> +
>> +uintptr_t GuardedPoolAllocator::allocationSlotOffset(size_t Size) const
>> +{
>> +  assert(Size > 0);
>> +
>> +  bool ShouldRightAlign = getRandomUnsigned32() % 2 == 0;  if
>> + (!ShouldRightAlign)
>> +    return 0;
>> +
>> +  uintptr_t Offset = maximumAllocationSize();
>> +  if (!PerfectlyRightAlign) {
>> +    if (Size == 3)
>> +      Size = 4;
>> +    else if (Size > 4 && Size <= 8)
>> +      Size = 8;
>> +    else if (Size > 8 && (Size % 16) != 0)
>> +      Size += 16 - (Size % 16);
>> +  }
>> +  Offset -= Size;
>> +  return Offset;
>> +}
>> +
>> +void GuardedPoolAllocator::reportError(uintptr_t AccessPtr, Error
>> +Error) {
>> +  if (SingletonPtr)
>> +    SingletonPtr->reportErrorInternal(AccessPtr, Error); }
>> +
>> +size_t GuardedPoolAllocator::getNearestSlot(uintptr_t Ptr) const {
>> +  if (Ptr <= GuardedPagePool + PageSize)
>> +    return 0;
>> +  if (Ptr > GuardedPagePoolEnd - PageSize)
>> +    return MaxSimultaneousAllocations - 1;
>> +
>> +  if (!isGuardPage(Ptr))
>> +    return addrToSlot(Ptr);
>> +
>> +  if (Ptr % PageSize <= PageSize / 2)
>> +    return addrToSlot(Ptr - PageSize); // Round down.
>> +  return addrToSlot(Ptr + PageSize);   // Round up.
>> +}
>> +
>> +Error GuardedPoolAllocator::diagnoseUnknownError(uintptr_t AccessPtr,
>> +                                                 AllocationMetadata
>> +**Meta) {
>> +  // Let's try and figure out what the source of this error is.
>> +  if (isGuardPage(AccessPtr)) {
>> +    size_t Slot = getNearestSlot(AccessPtr);
>> +    AllocationMetadata *SlotMeta = addrToMetadata(slotToAddr(Slot));
>> +
>> +    // Ensure that this slot was allocated once upon a time.
>> +    if (!SlotMeta->Addr)
>> +      return Error::UNKNOWN;
>> +    *Meta = SlotMeta;
>> +
>> +    if (SlotMeta->Addr < AccessPtr)
>> +      return Error::BUFFER_OVERFLOW;
>> +    return Error::BUFFER_UNDERFLOW;
>> +  }
>> +
>> +  // Access wasn't a guard page, check for use-after-free.
>> +  AllocationMetadata *SlotMeta = addrToMetadata(AccessPtr);  if
>> + (SlotMeta->IsDeallocated) {
>> +    *Meta = SlotMeta;
>> +    return Error::USE_AFTER_FREE;
>> +  }
>> +
>> +  // If we have reached here, the error is still unknown. There is no
>> +metadata
>> +  // available.
>> +  return Error::UNKNOWN;
>> +}
>> +
>> +// Prints the provided error and metadata information. Returns true if
>> +there is // additional context that can be provided, false otherwise
>> +(i.e. returns false // if Error == {UNKNOWN, INVALID_FREE without
>> metadata}).
>> +bool printErrorType(Error Error, uintptr_t AccessPtr, AllocationMetadata
>> *Meta,
>> +                    options::Printf_t Printf) {
>> +  switch (Error) {
>> +  case Error::UNKNOWN:
>> +    Printf("GWP-ASan couldn't automatically determine the source of the "
>> +           "memory error when accessing 0x%zx. It was likely caused by a
>> wild "
>> +           "memory access into the GWP-ASan pool.\n",
>> +           AccessPtr);
>> +    return false;
>> +  case Error::USE_AFTER_FREE:
>> +    Printf("Use after free occurred when accessing memory at: 0x%zx\n",
>> +           AccessPtr);
>> +    break;
>> +  case Error::DOUBLE_FREE:
>> +    Printf("Double free occurred when trying to free memory at: 0x%zx\n",
>> +           AccessPtr);
>> +    break;
>> +  case Error::INVALID_FREE:
>> +    Printf(
>> +        "Invalid (wild) free occurred when trying to free memory at:
>> 0x%zx\n",
>> +        AccessPtr);
>> +    // It's possible for an invalid free to fall onto a slot that has
>> never been
>> +    // allocated. If this is the case, there is no valid metadata.
>> +    if (Meta == nullptr)
>> +      return false;
>> +    break;
>> +  case Error::BUFFER_OVERFLOW:
>> +    Printf("Buffer overflow occurred when accessing memory at: 0x%zx\n",
>> +           AccessPtr);
>> +    break;
>> +  case Error::BUFFER_UNDERFLOW:
>> +    Printf("Buffer underflow occurred when accessing memory at: 0x%zx\n",
>> +           AccessPtr);
>> +    break;
>> +  }
>> +
>> +  Printf("0x%zx is ", AccessPtr);
>> +  if (AccessPtr < Meta->Addr)
>> +    Printf("located %zu bytes to the left of a %zu-byte allocation
>> located at "
>> +           "0x%zx\n",
>> +           Meta->Addr - AccessPtr, Meta->Size, Meta->Addr);
>> +  else if (AccessPtr > Meta->Addr)
>> +    Printf("located %zu bytes to the right of a %zu-byte allocation
>> located at "
>> +           "0x%zx\n",
>> +           AccessPtr - Meta->Addr, Meta->Size, Meta->Addr);
>> +  else
>> +    Printf("a %zu-byte allocation\n", Meta->Size);
>> +  return true;
>> +}
>> +
>> +void printThreadInformation(Error Error, uintptr_t AccessPtr,
>> +                            AllocationMetadata *Meta,
>> +                            options::Printf_t Printf) {
>> +  Printf("0x%zx was allocated by thread ", AccessPtr);
>> +  if (Meta->AllocationTrace.ThreadID == UINT64_MAX)
>> +    Printf("UNKNOWN.\n");
>> +  else
>> +    Printf("%zu.\n", Meta->AllocationTrace.ThreadID);
>> +
>> +  if (Error == Error::USE_AFTER_FREE || Error == Error::DOUBLE_FREE) {
>> +    Printf("0x%zx was freed by thread ", AccessPtr);
>> +    if (Meta->AllocationTrace.ThreadID == UINT64_MAX)
>> +      Printf("UNKNOWN.\n");
>> +    else
>> +      Printf("%zu.\n", Meta->AllocationTrace.ThreadID);
>> +  }
>> +}
>> +
>> +struct ScopedEndOfReportDecorator {
>> +  ScopedEndOfReportDecorator(options::Printf_t Printf) : Printf(Printf)
>> +{}
>> +  ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report
>> +***\n"); }
>> +  options::Printf_t Printf;
>> +};
>> +
>> +void GuardedPoolAllocator::reportErrorInternal(uintptr_t AccessPtr,
>> +                                               Error Error) {
>> +  if (!pointerIsMine(reinterpret_cast<void *>(AccessPtr))) {
>> +    return;
>> +  }
>> +
>> +  // Attempt to prevent races to re-use the same slot that triggered
>> this error.
>> +  // This does not guarantee that there are no races, because another
>> + thread can  // take the locks during the time that the signal handler
>> is being called.
>> +  PoolMutex.tryLock();
>> +
>> +  Printf("*** GWP-ASan detected a memory error ***\n");
>> + ScopedEndOfReportDecorator Decorator(Printf);
>> +
>> +  AllocationMetadata *Meta = nullptr;
>> +
>> +  if (Error == Error::UNKNOWN) {
>> +    Error = diagnoseUnknownError(AccessPtr, &Meta);  } else {
>> +    size_t Slot = getNearestSlot(AccessPtr);
>> +    Meta = addrToMetadata(slotToAddr(Slot));
>> +    // Ensure that this slot has been previously allocated.
>> +    if (!Meta->Addr)
>> +      Meta = nullptr;
>> +  }
>> +
>> +  // Print the error information, and if there is no valid metadata,
>> stop here.
>> +  if (!printErrorType(Error, AccessPtr, Meta, Printf)) {
>> +    return;
>> +  }
>> +
>> +  // Ensure that we have a valid metadata pointer from this point
>> forward.
>> +  if (Meta == nullptr) {
>> +    Printf("GWP-ASan internal unreachable error. Metadata is not
>> null.\n");
>> +    return;
>> +  }
>> +
>> +  printThreadInformation(Error, AccessPtr, Meta, Printf);
>> +  // TODO(hctim): Implement stack unwinding here. Ask the caller to
>> +provide us
>> +  // with the base pointer, and we unwind the stack to give a stack
>> +trace for
>> +  // the access.
>> +  // TODO(hctim): Implement dumping here of allocation/deallocation
>> traces.
>> +}
>> +
>> +TLS_INITIAL_EXEC uint64_t GuardedPoolAllocator::NextSampleCounter = 0;
>> +} // namespace gwp_asan
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.h?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.h (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/guarded_pool_allocator.h Wed Jun  5
>> +++ 12:42:48 2019
>> @@ -0,0 +1,254 @@
>> +//===-- guarded_pool_allocator.h --------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
>> +#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
>> +
>> +#include "gwp_asan/definitions.h"
>> +#include "gwp_asan/mutex.h"
>> +#include "gwp_asan/options.h"
>> +#include "gwp_asan/random.h"
>> +
>> +#include <stddef.h>
>> +#include <stdint.h>
>> +
>> +namespace gwp_asan {
>> +// This class is the primary implementation of the allocator portion of
>> +GWP- // ASan. It is the sole owner of the pool of sequentially
>> +allocated guarded // slots. It should always be treated as a singleton.
>> +
>> +// Functions in the public interface of this class are
>> +thread-compatible until // init() is called, at which point they become
>> +thread-safe (unless specified // otherwise).
>> +class GuardedPoolAllocator {
>> +public:
>> +  static constexpr uint64_t kInvalidThreadID = UINT64_MAX;
>> +
>> +  enum class Error {
>> +    UNKNOWN,
>> +    USE_AFTER_FREE,
>> +    DOUBLE_FREE,
>> +    INVALID_FREE,
>> +    BUFFER_OVERFLOW,
>> +    BUFFER_UNDERFLOW
>> +  };
>> +
>> +  struct AllocationMetadata {
>> +    // Maximum number of stack trace frames to collect for allocations +
>> frees.
>> +    // TODO(hctim): Implement stack frame compression, a-la Chromium.
>> +    // Currently the maximum stack frames is one, as we don't collect
>> traces.
>> +    static constexpr size_t kMaximumStackFrames = 1;
>> +
>> +    // Records the given allocation metadata into this struct. In the
>> future,
>> +    // this will collect the allocation trace as well.
>> +    void RecordAllocation(uintptr_t Addr, size_t Size);
>> +
>> +    // Record that this allocation is now deallocated. In future, this
>> will
>> +    // collect the deallocation trace as well.
>> +    void RecordDeallocation();
>> +
>> +    struct CallSiteInfo {
>> +      // The backtrace to the allocation/deallocation. If the first
>> value is
>> +      // zero, we did not collect a trace.
>> +      uintptr_t Trace[kMaximumStackFrames] = {};
>> +      // The thread ID for this trace, or kInvalidThreadID if not
>> available.
>> +      uint64_t ThreadID = kInvalidThreadID;
>> +    };
>> +
>> +    // The address of this allocation.
>> +    uintptr_t Addr = 0;
>> +    // Represents the actual size of the allocation.
>> +    size_t Size = 0;
>> +
>> +    CallSiteInfo AllocationTrace;
>> +    CallSiteInfo DeallocationTrace;
>> +
>> +    // Whether this allocation has been deallocated yet.
>> +    bool IsDeallocated = false;
>> +  };
>> +
>> +  // During program startup, we must ensure that memory allocations do
>> + not land  // in this allocation pool if the allocator decides to
>> + runtime-disable  // GWP-ASan. The constructor value-initialises the
>> + class such that if no  // further initialisation takes place, calls to
>> + shouldSample() and  // pointerIsMine() will return false.
>> +  constexpr GuardedPoolAllocator(){};
>> +  GuardedPoolAllocator(const GuardedPoolAllocator &) = delete;
>> + GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) =
>> + delete;
>> +
>> +  // Note: This class is expected to be a singleton for the lifetime of
>> + the  // program. If this object is initialised, it will leak the
>> + guarded page pool  // and metadata allocations during destruction. We
>> + can't clean up these areas  // as this may cause a use-after-free on
>> shutdown.
>> +  ~GuardedPoolAllocator() = default;
>> +
>> +  // Initialise the rest of the members of this class. Create the
>> + allocation  // pool using the provided options. See options.inc for
>> + runtime configuration  // options.
>> +  void init(const options::Options &Opts);
>> +
>> +  // Return whether the allocation should be randomly chosen for
>> sampling.
>> +  ALWAYS_INLINE bool shouldSample() {
>> +    // NextSampleCounter == 0 means we "should regenerate the counter".
>> +    //                   == 1 means we "should sample this allocation".
>> +    if (UNLIKELY(NextSampleCounter == 0)) {
>> +      // GuardedPagePoolEnd == 0 if GWP-ASan is disabled.
>> +      if (UNLIKELY(GuardedPagePoolEnd == 0))
>> +        return false;
>> +      NextSampleCounter = (getRandomUnsigned32() % AdjustedSampleRate) +
>> 1;
>> +    }
>> +
>> +    return UNLIKELY(--NextSampleCounter == 0);  }
>> +
>> +  // Returns whether the provided pointer is a current sampled
>> + allocation that  // is owned by this pool.
>> +  ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
>> +    uintptr_t P = reinterpret_cast<uintptr_t>(Ptr);
>> +    return GuardedPagePool <= P && P < GuardedPagePoolEnd;  }
>> +
>> +  // Allocate memory in a guarded slot, and return a pointer to the new
>> + // allocation. Returns nullptr if the pool is empty, the requested
>> + size is too  // large for this pool to handle, or the requested size is
>> zero.
>> +  void *allocate(size_t Size);
>> +
>> +  // Deallocate memory in a guarded slot. The provided pointer must
>> + have been  // allocated using this pool. This will set the guarded slot
>> as inaccessible.
>> +  void deallocate(void *Ptr);
>> +
>> +  // Returns the size of the allocation at Ptr.
>> +  size_t getSize(const void *Ptr);
>> +
>> +  // Returns the largest allocation that is supported by this pool. Any
>> + // allocations larger than this should go to the regular system
>> allocator.
>> +  size_t maximumAllocationSize() const;
>> +
>> +  // Dumps an error report (including allocation and deallocation stack
>> traces).
>> +  // An optional error may be provided if the caller knows what the
>> + error is  // ahead of time. This is primarily a helper function to
>> + locate the static  // singleton pointer and call the internal version
>> + of this function. This  // method is never thread safe, and should
>> + only be called when fatal errors  // occur.
>> +  static void reportError(uintptr_t AccessPtr, Error Error =
>> + Error::UNKNOWN);
>> +
>> +private:
>> +  static constexpr size_t kInvalidSlotID = SIZE_MAX;
>> +
>> +  // These functions anonymously map memory or change the permissions
>> + of mapped  // memory into this process in a platform-specific way.
>> + Pointer and size  // arguments are expected to be page-aligned. These
>> + functions will never  // return on error, instead electing to kill the
>> calling process on failure.
>> +  // Note that memory is initially mapped inaccessible. In order for RW
>> + // mappings, call mapMemory() followed by markReadWrite() on the
>> + returned  // pointer.
>> +  void *mapMemory(size_t Size) const;
>> +  void markReadWrite(void *Ptr, size_t Size) const;  void
>> + markInaccessible(void *Ptr, size_t Size) const;
>> +
>> +  // Get the current thread ID, or kInvalidThreadID if failure. Note:
>> + This  // implementation is platform-specific.
>> +  static uint64_t getThreadID();
>> +
>> +  // Get the page size from the platform-specific implementation. Only
>> + needs to  // be called once, and the result should be cached in
>> PageSize in this class.
>> +  static size_t getPlatformPageSize();
>> +
>> +  // Install the SIGSEGV crash handler for printing use-after-free and
>> + heap-  // buffer-{under|over}flow exceptions. This is platform
>> + specific as even  // though POSIX and Windows both support registering
>> + handlers through  // signal(), we have to use platform-specific signal
>> + handlers to obtain the  // address that caused the SIGSEGV exception.
>> +  static void installSignalHandlers();
>> +
>> +  // Returns the index of the slot that this pointer resides in. If the
>> + pointer  // is not owned by this pool, the result is undefined.
>> +  size_t addrToSlot(uintptr_t Ptr) const;
>> +
>> +  // Returns the address of the N-th guarded slot.
>> +  uintptr_t slotToAddr(size_t N) const;
>> +
>> +  // Returns a pointer to the metadata for the owned pointer. If the
>> + pointer is  // not owned by this pool, the result is undefined.
>> +  AllocationMetadata *addrToMetadata(uintptr_t Ptr) const;
>> +
>> +  // Returns the address of the page that this pointer resides in.
>> +  uintptr_t getPageAddr(uintptr_t Ptr) const;
>> +
>> +  // Gets the nearest slot to the provided address.
>> +  size_t getNearestSlot(uintptr_t Ptr) const;
>> +
>> +  // Returns whether the provided pointer is a guard page or not. The
>> + pointer  // must be within memory owned by this pool, else the result
>> is undefined.
>> +  bool isGuardPage(uintptr_t Ptr) const;
>> +
>> +  // Reserve a slot for a new guarded allocation. Returns
>> + kInvalidSlotID if no  // slot is available to be reserved.
>> +  size_t reserveSlot();
>> +
>> +  // Unreserve the guarded slot.
>> +  void freeSlot(size_t SlotIndex);
>> +
>> +  // Returns the offset (in bytes) between the start of a guarded slot
>> + and where  // the start of the allocation should take place.
>> + Determined using the size of  // the allocation and the options
>> provided at init-time.
>> +  uintptr_t allocationSlotOffset(size_t AllocationSize) const;
>> +
>> +  // Returns the diagnosis for an unknown error. If the diagnosis is
>> + not  // Error::INVALID_FREE or Error::UNKNOWN, the metadata for the
>> + slot  // responsible for the error is placed in *Meta.
>> +  Error diagnoseUnknownError(uintptr_t AccessPtr, AllocationMetadata
>> + **Meta);
>> +
>> +  void reportErrorInternal(uintptr_t AccessPtr, Error Error);
>> +
>> +  // Cached page size for this system in bytes.
>> +  size_t PageSize = 0;
>> +
>> +  // A mutex to protect the guarded slot and metadata pool for this
>> class.
>> +  Mutex PoolMutex;
>> +  // The number of guarded slots that this pool holds.
>> +  size_t MaxSimultaneousAllocations = 0;  // Record the number
>> + allocations that we've sampled. We store this amount so  // that we
>> + don't randomly choose to recycle a slot that previously had an  //
>> + allocation before all the slots have been utilised.
>> +  size_t NumSampledAllocations = 0;
>> +  // Pointer to the pool of guarded slots. Note that this points to the
>> + start of  // the pool (which is a guard page), not a pointer to the
>> first guarded page.
>> +  uintptr_t GuardedPagePool = UINTPTR_MAX;  uintptr_t
>> + GuardedPagePoolEnd = 0;  // Pointer to the allocation metadata
>> + (allocation/deallocation stack traces),  // if any.
>> +  AllocationMetadata *Metadata = nullptr;
>> +
>> +  // Pointer to an array of free slot indexes.
>> +  size_t *FreeSlots = nullptr;
>> +  // The current length of the list of free slots.
>> +  size_t FreeSlotsLength = 0;
>> +
>> +  // See options.{h, inc} for more information.
>> +  bool PerfectlyRightAlign = false;
>> +
>> +  // Printf function supplied by the implementing allocator. We can't
>> + (in  // general) use printf() from the cstdlib as it may malloc(),
>> + causing infinite  // recursion.
>> +  options::Printf_t Printf = nullptr;
>> +
>> +  // The adjusted sample rate for allocation sampling. Default *must*
>> +be
>> +  // nonzero, as dynamic initialisation may call malloc (e.g. from
>> +libstdc++)
>> +  // before GPA::init() is called. This would cause an error in
>> +shouldSample(),
>> +  // where we would calculate modulo zero. This value is set
>> +UINT32_MAX, as when
>> +  // GWP-ASan is disabled, we wish to never spend wasted cycles
>> +recalculating
>> +  // the sample rate.
>> +  uint32_t AdjustedSampleRate = UINT32_MAX;
>> +  // Thread-local decrementing counter that indicates that a given
>> +allocation
>> +  // should be sampled when it reaches zero.
>> +  static TLS_INITIAL_EXEC uint64_t NextSampleCounter; }; } // namespace
>> +gwp_asan
>> +
>> +#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
>>
>> Added:
>> compiler-rt/trunk/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp?rev=362636&view=auto
>>
>> ==============================================================================
>> ---
>> compiler-rt/trunk/lib/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
>> (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/platform_specific/guarded_pool_alloca
>> +++ tor_posix.cpp Wed Jun  5 12:42:48 2019
>> @@ -0,0 +1,96 @@
>> +//===-- guarded_pool_allocator_posix.cpp ------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#include "gwp_asan/guarded_pool_allocator.h"
>> +
>> +#include <stdlib.h>
>> +#include <errno.h>
>> +#include <signal.h>
>> +#include <sys/mman.h>
>> +#include <sys/syscall.h>
>> +#include <sys/types.h>
>> +#include <unistd.h>
>> +
>> +namespace gwp_asan {
>> +
>> +void *GuardedPoolAllocator::mapMemory(size_t Size) const {
>> +  void *Ptr =
>> +      mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1,
>> +0);
>> +
>> +  if (Ptr == MAP_FAILED) {
>> +    Printf("Failed to map guarded pool allocator memory, errno: %d\n",
>> errno);
>> +    Printf("  mmap(nullptr, %zu, ...) failed.\n", Size);
>> +    exit(EXIT_FAILURE);
>> +  }
>> +  return Ptr;
>> +}
>> +
>> +void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size) const
>> +{
>> +  if (mprotect(Ptr, Size, PROT_READ | PROT_WRITE) != 0) {
>> +    Printf("Failed to set guarded pool allocator memory at as RW, errno:
>> %d\n",
>> +           errno);
>> +    Printf("  mprotect(%p, %zu, RW) failed.\n", Ptr, Size);
>> +    exit(EXIT_FAILURE);
>> +  }
>> +}
>> +
>> +void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size)
>> +const {
>> +  // mmap() a PROT_NONE page over the address to release it to the
>> +system, if
>> +  // we used mprotect() here the system would count pages in the
>> +quarantine
>> +  // against the RSS.
>> +  if (mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS |
>> MAP_PRIVATE, -1,
>> +           0) == MAP_FAILED) {
>> +    Printf("Failed to set guarded pool allocator memory as inaccessible,
>> "
>> +           "errno: %d\n",
>> +           errno);
>> +    Printf("  mmap(%p, %zu, NONE, ...) failed.\n", Ptr, Size);
>> +    exit(EXIT_FAILURE);
>> +  }
>> +}
>> +
>> +size_t GuardedPoolAllocator::getPlatformPageSize() {
>> +  return sysconf(_SC_PAGESIZE);
>> +}
>> +
>> +struct sigaction PreviousHandler;
>> +
>> +static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
>> +  gwp_asan::GuardedPoolAllocator::reportError(
>> +      reinterpret_cast<uintptr_t>(info->si_addr));
>> +
>> +  // Process any previous handlers.
>> +  if (PreviousHandler.sa_flags & SA_SIGINFO) {
>> +    PreviousHandler.sa_sigaction(sig, info, ucontext);
>> +  } else if (PreviousHandler.sa_handler == SIG_IGN ||
>> +             PreviousHandler.sa_handler == SIG_DFL) {
>> +    // If the previous handler was the default handler, or was ignoring
>> this
>> +    // signal, install the default handler and re-raise the signal in
>> order to
>> +    // get a core dump and terminate this process.
>> +    signal(SIGSEGV, SIG_DFL);
>> +    raise(SIGSEGV);
>> +  } else {
>> +    PreviousHandler.sa_handler(sig);
>> +  }
>> +}
>> +
>> +void GuardedPoolAllocator::installSignalHandlers() {
>> +  struct sigaction Action;
>> +  Action.sa_sigaction = sigSegvHandler;
>> +  Action.sa_flags = SA_SIGINFO;
>> +  sigaction(SIGSEGV, &Action, &PreviousHandler); }
>> +
>> +uint64_t GuardedPoolAllocator::getThreadID() { #ifdef SYS_gettid
>> +  return syscall(SYS_gettid);
>> +#else
>> +  return kInvalidThreadID;
>> +#endif
>> +}
>> +
>> +} // namespace gwp_asan
>>
>> Modified: compiler-rt/trunk/lib/gwp_asan/tests/CMakeLists.txt
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/tests/CMakeLists.txt?rev=362636&r1=362635&r2=362636&view=diff
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/tests/CMakeLists.txt (original)
>> +++ compiler-rt/trunk/lib/gwp_asan/tests/CMakeLists.txt Wed Jun  5
>> +++ 12:42:48 2019
>> @@ -9,7 +9,8 @@ set(GWP_ASAN_UNITTEST_CFLAGS  file(GLOB GWP_ASAN_HEADERS
>> ../*.h)  file(GLOB GWP_ASAN_UNITTESTS *.cpp)  set(GWP_ASAN_UNIT_TEST_HEADERS
>> -  ${GWP_ASAN_HEADERS})
>> +  ${GWP_ASAN_HEADERS}
>> +  harness.h)
>>
>>  add_custom_target(GwpAsanUnitTests)
>>  set_target_properties(GwpAsanUnitTests PROPERTIES FOLDER "Compiler-RT
>> Tests") @@ -26,8 +27,11 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LI
>>
>>    set(GWP_ASAN_TEST_RUNTIME RTGwpAsanTest.${arch})
>>
>> +  # RTSanitizerCommonNoTermination(NoLibc) required for
>> __sanitizer::Printf.
>>    set(GWP_ASAN_TEST_RUNTIME_OBJECTS
>> -    $<TARGET_OBJECTS:RTGwpAsan.${arch}>)
>> +    $<TARGET_OBJECTS:RTGwpAsan.${arch}>
>> +    $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
>> +    $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.${arch}>)
>>
>>    add_library(${GWP_ASAN_TEST_RUNTIME} STATIC
>>      ${GWP_ASAN_TEST_RUNTIME_OBJECTS})
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/tests/alignment.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/tests/alignment.cpp?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/tests/alignment.cpp (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/tests/alignment.cpp Wed Jun  5
>> +++ 12:42:48 2019
>> @@ -0,0 +1,27 @@
>> +//===-- alignment.cc --------------------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#include "gwp_asan/tests/harness.h"
>> +
>> +TEST_F(DefaultGuardedPoolAllocator, BasicAllocation) {
>> +  std::vector<std::pair<int, int>> AllocSizeToAlignment = {
>> +      {1, 1},   {2, 2},   {3, 4},       {4, 4},       {5, 8},   {7, 8},
>> +      {8, 8},   {9, 16},  {15, 16},     {16, 16},     {17, 16}, {31, 16},
>> +      {32, 16}, {33, 16}, {4095, 4096}, {4096, 4096},
>> +  };
>> +
>> +  for (const auto &KV : AllocSizeToAlignment) {
>> +    void *Ptr = GPA.allocate(KV.first);
>> +    EXPECT_NE(nullptr, Ptr);
>> +
>> +    // Check the alignment of the pointer is as expected.
>> +    EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(Ptr) % KV.second);
>> +
>> +    GPA.deallocate(Ptr);
>> +  }
>> +}
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/tests/basic.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/tests/basic.cpp?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/tests/basic.cpp (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/tests/basic.cpp Wed Jun  5 12:42:48
>> +++ 2019
>> @@ -0,0 +1,60 @@
>> +//===-- basic.cc ------------------------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#include "gwp_asan/tests/harness.h"
>> +
>> +TEST_F(CustomGuardedPoolAllocator, BasicAllocation) {
>> +  InitNumSlots(1);
>> +  void *Ptr = GPA.allocate(1);
>> +  EXPECT_NE(nullptr, Ptr);
>> +  EXPECT_TRUE(GPA.pointerIsMine(Ptr));
>> +  EXPECT_EQ(1u, GPA.getSize(Ptr));
>> +  GPA.deallocate(Ptr);
>> +}
>> +
>> +TEST_F(DefaultGuardedPoolAllocator, NullptrIsNotMine) {
>> +  EXPECT_FALSE(GPA.pointerIsMine(nullptr));
>> +}
>> +
>> +TEST_F(CustomGuardedPoolAllocator, SizedAllocations) {
>> +  InitNumSlots(1);
>> +
>> +  std::size_t MaxAllocSize = GPA.maximumAllocationSize();
>> + EXPECT_TRUE(MaxAllocSize > 0);
>> +
>> +  for (unsigned AllocSize = 1; AllocSize <= MaxAllocSize; AllocSize <<=
>> 1) {
>> +    void *Ptr = GPA.allocate(AllocSize);
>> +    EXPECT_NE(nullptr, Ptr);
>> +    EXPECT_TRUE(GPA.pointerIsMine(Ptr));
>> +    EXPECT_EQ(AllocSize, GPA.getSize(Ptr));
>> +    GPA.deallocate(Ptr);
>> +  }
>> +}
>> +
>> +TEST_F(DefaultGuardedPoolAllocator, TooLargeAllocation) {
>> +  EXPECT_EQ(nullptr, GPA.allocate(GPA.maximumAllocationSize() + 1)); }
>> +
>> +TEST_F(CustomGuardedPoolAllocator, AllocAllSlots) {
>> +  constexpr unsigned kNumSlots = 128;
>> +  InitNumSlots(kNumSlots);
>> +  void *Ptrs[kNumSlots];
>> +  for (unsigned i = 0; i < kNumSlots; ++i) {
>> +    Ptrs[i] = GPA.allocate(1);
>> +    EXPECT_NE(nullptr, Ptrs[i]);
>> +    EXPECT_TRUE(GPA.pointerIsMine(Ptrs[i]));
>> +  }
>> +
>> +  // This allocation should fail as all the slots are used.
>> +  void *Ptr = GPA.allocate(1);
>> +  EXPECT_EQ(nullptr, Ptr);
>> +  EXPECT_FALSE(GPA.pointerIsMine(nullptr));
>> +
>> +  for (unsigned i = 0; i < kNumSlots; ++i)
>> +    GPA.deallocate(Ptrs[i]);
>> +}
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/tests/harness.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/tests/harness.h?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/tests/harness.h (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/tests/harness.h Wed Jun  5 12:42:48
>> +++ 2019
>> @@ -0,0 +1,60 @@
>> +//===-- harness.h -----------------------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#ifndef GWP_ASAN_TESTS_HARNESS_H_
>> +#define GWP_ASAN_TESTS_HARNESS_H_
>> +
>> +#include "gtest/gtest.h"
>> +
>> +// Include sanitizer_common first as gwp_asan/guarded_pool_allocator.h
>> +// transiently includes definitions.h, which overwrites some of the
>> +definitions // in sanitizer_common.
>> +#include "sanitizer_common/sanitizer_common.h"
>> +
>> +#include "gwp_asan/guarded_pool_allocator.h"
>> +#include "gwp_asan/options.h"
>> +
>> +class DefaultGuardedPoolAllocator : public ::testing::Test {
>> +public:
>> +  DefaultGuardedPoolAllocator() {
>> +    gwp_asan::options::Options Opts;
>> +    Opts.setDefaults();
>> +    MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
>> +
>> +    Opts.Printf = __sanitizer::Printf;
>> +    GPA.init(Opts);
>> +  }
>> +
>> +protected:
>> +  gwp_asan::GuardedPoolAllocator GPA;
>> +  decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
>> +      MaxSimultaneousAllocations;
>> +};
>> +
>> +class CustomGuardedPoolAllocator : public ::testing::Test {
>> +public:
>> +  void
>> +
>> InitNumSlots(decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
>> +                   MaxSimultaneousAllocationsArg) {
>> +    gwp_asan::options::Options Opts;
>> +    Opts.setDefaults();
>> +
>> +    Opts.MaxSimultaneousAllocations = MaxSimultaneousAllocationsArg;
>> +    MaxSimultaneousAllocations = MaxSimultaneousAllocationsArg;
>> +
>> +    Opts.Printf = __sanitizer::Printf;
>> +    GPA.init(Opts);
>> +  }
>> +
>> +protected:
>> +  gwp_asan::GuardedPoolAllocator GPA;
>> +  decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
>> +      MaxSimultaneousAllocations;
>> +};
>> +
>> +#endif // GWP_ASAN_TESTS_HARNESS_H_
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/tests/slot_reuse.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/tests/slot_reuse.cpp?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/tests/slot_reuse.cpp (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/tests/slot_reuse.cpp Wed Jun  5
>> +++ 12:42:48 2019
>> @@ -0,0 +1,72 @@
>> +//===-- slot_reuse.cc -------------------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#include "gwp_asan/tests/harness.h"
>> +
>> +void singleByteGoodAllocDealloc(gwp_asan::GuardedPoolAllocator *GPA) {
>> +  void *Ptr = GPA->allocate(1);
>> +  EXPECT_NE(nullptr, Ptr);
>> +  EXPECT_TRUE(GPA->pointerIsMine(Ptr));
>> +  EXPECT_EQ(1u, GPA->getSize(Ptr));
>> +  GPA->deallocate(Ptr);
>> +}
>> +
>> +TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine1) {
>> +  InitNumSlots(1);
>> +  for (unsigned i = 0; i < 128; ++i)
>> +    singleByteGoodAllocDealloc(&GPA);
>> +}
>> +
>> +TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine2) {
>> +  InitNumSlots(2);
>> +  for (unsigned i = 0; i < 128; ++i)
>> +    singleByteGoodAllocDealloc(&GPA);
>> +}
>> +
>> +TEST_F(CustomGuardedPoolAllocator, EnsureReuseOfQuarantine127) {
>> +  InitNumSlots(127);
>> +  for (unsigned i = 0; i < 128; ++i)
>> +    singleByteGoodAllocDealloc(&GPA);
>> +}
>> +
>> +// This test ensures that our slots are not reused ahead of time. We
>> +increase // the use-after-free detection by not reusing slots until all
>> +of them have been // allocated. This is done by always using the slots
>> +from left-to-right in the // pool before we used each slot once, at
>> +which point random selection takes // over.
>> +void runNoReuseBeforeNecessary(gwp_asan::GuardedPoolAllocator *GPA,
>> +                               unsigned PoolSize) {
>> +  std::set<void *> Ptrs;
>> +  for (unsigned i = 0; i < PoolSize; ++i) {
>> +    void *Ptr = GPA->allocate(1);
>> +
>> +    EXPECT_TRUE(GPA->pointerIsMine(Ptr));
>> +    EXPECT_EQ(0u, Ptrs.count(Ptr));
>> +
>> +    Ptrs.insert(Ptr);
>> +    GPA->deallocate(Ptr);
>> +  }
>> +}
>> +
>> +TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary2) {
>> +  constexpr unsigned kPoolSize = 2;
>> +  InitNumSlots(kPoolSize);
>> +  runNoReuseBeforeNecessary(&GPA, kPoolSize); }
>> +
>> +TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary128) {
>> +  constexpr unsigned kPoolSize = 128;
>> +  InitNumSlots(kPoolSize);
>> +  runNoReuseBeforeNecessary(&GPA, kPoolSize); }
>> +
>> +TEST_F(CustomGuardedPoolAllocator, NoReuseBeforeNecessary129) {
>> +  constexpr unsigned kPoolSize = 129;
>> +  InitNumSlots(kPoolSize);
>> +  runNoReuseBeforeNecessary(&GPA, kPoolSize); }
>>
>> Added: compiler-rt/trunk/lib/gwp_asan/tests/thread_contention.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/gwp_asan/tests/thread_contention.cpp?rev=362636&view=auto
>>
>> ==============================================================================
>> --- compiler-rt/trunk/lib/gwp_asan/tests/thread_contention.cpp (added)
>> +++ compiler-rt/trunk/lib/gwp_asan/tests/thread_contention.cpp Wed Jun
>> +++ 5 12:42:48 2019
>> @@ -0,0 +1,69 @@
>> +//===-- thread_contention.cc ------------------------------------*- C++
>> +-*-===// // // Part of the LLVM Project, under the Apache License v2.0
>> +with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //
>> +//===------------------------------------------------------------------
>> +----===//
>> +
>> +#include "gwp_asan/tests/harness.h"
>> +
>> +// Note: Compilation of <atomic> and <thread> are extremely expensive
>> +for // non-opt builds of clang.
>> +#include <atomic>
>> +#include <cstdlib>
>> +#include <thread>
>> +#include <vector>
>> +
>> +void asyncTask(gwp_asan::GuardedPoolAllocator *GPA,
>> +               std::atomic<bool> *StartingGun, unsigned NumIterations)
>> +{
>> +  while (!*StartingGun) {
>> +    // Wait for starting gun.
>> +  }
>> +
>> +  // Get ourselves a new allocation.
>> +  for (unsigned i = 0; i < NumIterations; ++i) {
>> +    volatile char *Ptr = reinterpret_cast<volatile char *>(
>> +        GPA->allocate(GPA->maximumAllocationSize()));
>> +    // Do any other threads have access to this page?
>> +    EXPECT_EQ(*Ptr, 0);
>> +
>> +    // Mark the page as from malloc. Wait to see if another thread also
>> takes
>> +    // this page.
>> +    *Ptr = 'A';
>> +    std::this_thread::sleep_for(std::chrono::nanoseconds(10000));
>> +
>> +    // Check we still own the page.
>> +    EXPECT_EQ(*Ptr, 'A');
>> +
>> +    // And now release it.
>> +    *Ptr = 0;
>> +    GPA->deallocate(const_cast<char *>(Ptr));
>> +  }
>> +}
>> +
>> +void runThreadContentionTest(unsigned NumThreads, unsigned NumIterations,
>> +                             gwp_asan::GuardedPoolAllocator *GPA) {
>> +
>> +  std::atomic<bool> StartingGun{false};  std::vector<std::thread>
>> + Threads;  if (std::thread::hardware_concurrency() < NumThreads) {
>> +    NumThreads = std::thread::hardware_concurrency();
>> +  }
>> +
>> +  for (unsigned i = 0; i < NumThreads; ++i) {
>> +    Threads.emplace_back(asyncTask, GPA, &StartingGun, NumIterations);
>> + }
>> +
>> +  StartingGun = true;
>> +
>> +  for (auto &T : Threads)
>> +    T.join();
>> +}
>> +
>> +TEST_F(CustomGuardedPoolAllocator, ThreadContention) {
>> +  unsigned NumThreads = 4;
>> +  unsigned NumIterations = 10000;
>> +  InitNumSlots(NumThreads);
>> +  runThreadContentionTest(NumThreads, NumIterations, &GPA); }
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20190605/9d7d58d6/attachment-0001.html>


More information about the llvm-commits mailing list