[compiler-rt] r362636 - [GWP-ASan] Core Guarded Pool Allocator [4].
Mitch Phillips via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 6 09:27:08 PDT 2019
LGTM. Thanks for doing that.
On Thu, Jun 6, 2019 at 1:47 AM <douglas.yung at sony.com> wrote:
> Thanks for looking into this Mitch. I’ve attempted to make the fix you
> suggested in r362682 and it seems to be working so far. Please review it
> when you have a chance to make sure I didn’t break anything. Thanks again!
>
>
>
> Douglas Yung
>
>
>
> *From:* Mitch Phillips <mitchphillips at outlook.com>
> *Sent:* Wednesday, June 5, 2019 20:46
> *To:* Yung, Douglas <douglas.yung at sony.com>
> *Cc:* llvm-commits <llvm-commits at lists.llvm.org>
> *Subject:* Re: [compiler-rt] r362636 - [GWP-ASan] Core Guarded Pool
> Allocator [4].
>
>
>
> Alright so I've got this nailed down. Looks like gcc 5.4 struggles with
> the variable being the same name as the typedef. It fails if the identifier
> is the same as the alias. https://godbolt.org/z/yV0Y_Y reproduces the
> issue, as line 16 uses "Error" as both the aliased type, and the variable
> identifier. By switching the identifier of the variable to "E", it fixes
> itself: https://godbolt.org/z/GOX4cR.
>
>
>
> I still can't get myself to a machine I can commit from, if it's possible
> to patch this and unblock yourselves, please do. Otherwise, I'll follow up
> tomorrow morning (around 13h from now).
>
>
>
> On Wed, Jun 5, 2019 at 8:11 PM Mitch Phillips <mitchphillips at outlook.com>
> wrote:
>
> 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/20190606/cb2cbfd1/attachment.html>
More information about the llvm-commits
mailing list