[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