[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:10:05 PDT 2019


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/1984523c/attachment.html>


More information about the llvm-commits mailing list