Mark ab/6881855 as merged
Bug: 172690556
Change-Id: I532bddab7df3265eae54bf4775bb656c3dfba639
diff --git a/Android.bp b/Android.bp
index f9f439e..5bc4fb2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -21,6 +21,7 @@
recovery_available: true,
native_bridge_supported: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
// GWP-ASan currently has no support for darwin.
target: {
@@ -56,8 +57,8 @@
"//apex_available:platform",
"com.android.runtime",
// GWP-ASan headers are currently referenced by the following additional APEXes
+ "com.android.art",
"com.android.art.debug",
- "com.android.art.release",
"com.android.media",
"com.android.media.swcodec",
],
@@ -78,8 +79,8 @@
"gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp",
"gwp_asan/platform_specific/mutex_posix.cpp",
"gwp_asan/platform_specific/utilities_posix.cpp",
- "gwp_asan/random.cpp",
"gwp_asan/stack_trace_compressor.cpp",
+ "gwp_asan/utilities.cpp"
],
// GWP-ASan requires platform (non-emulated) TLS. We use thread local
// variables in the core, inlined interface to GWP-ASan.
diff --git a/TEST_MAPPING b/TEST_MAPPING
index a4db69b..4bb0d28 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -2,6 +2,14 @@
"presubmit": [
{
"name": "gwp_asan_unittest"
+ },
+ {
+ "name": "CtsGwpAsanTestCases"
+ }
+ ],
+ "imports": [
+ {
+ "path": "bionic"
}
]
}
diff --git a/gwp_asan/common.cpp b/gwp_asan/common.cpp
index 3438c4b..483694d 100644
--- a/gwp_asan/common.cpp
+++ b/gwp_asan/common.cpp
@@ -34,6 +34,9 @@
__builtin_trap();
}
+constexpr size_t AllocationMetadata::kStackFrameStorageBytes;
+constexpr size_t AllocationMetadata::kMaxTraceLengthToCollect;
+
void AllocationMetadata::RecordAllocation(uintptr_t AllocAddr,
size_t AllocSize) {
Addr = AllocAddr;
diff --git a/gwp_asan/crash_handler.cpp b/gwp_asan/crash_handler.cpp
index c3b9e14..b9baace 100644
--- a/gwp_asan/crash_handler.cpp
+++ b/gwp_asan/crash_handler.cpp
@@ -1,4 +1,4 @@
-//===-- crash_handler_interface.cpp -----------------------------*- C++ -*-===//
+//===-- crash_handler.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.
@@ -10,6 +10,7 @@
#include "gwp_asan/stack_trace_compressor.h"
#include <assert.h>
+#include <string.h>
using AllocationMetadata = gwp_asan::AllocationMetadata;
using Error = gwp_asan::Error;
@@ -112,9 +113,15 @@
size_t __gwp_asan_get_allocation_trace(
const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
size_t BufferLen) {
- return gwp_asan::compression::unpack(
+ uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
+ size_t UnpackedLength = gwp_asan::compression::unpack(
AllocationMeta->AllocationTrace.CompressedTrace,
- AllocationMeta->AllocationTrace.TraceSize, Buffer, BufferLen);
+ AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer,
+ AllocationMetadata::kMaxTraceLengthToCollect);
+ if (UnpackedLength < BufferLen)
+ BufferLen = UnpackedLength;
+ memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
+ return UnpackedLength;
}
bool __gwp_asan_is_deallocated(
@@ -130,9 +137,15 @@
size_t __gwp_asan_get_deallocation_trace(
const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
size_t BufferLen) {
- return gwp_asan::compression::unpack(
+ uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
+ size_t UnpackedLength = gwp_asan::compression::unpack(
AllocationMeta->DeallocationTrace.CompressedTrace,
- AllocationMeta->DeallocationTrace.TraceSize, Buffer, BufferLen);
+ AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer,
+ AllocationMetadata::kMaxTraceLengthToCollect);
+ if (UnpackedLength < BufferLen)
+ BufferLen = UnpackedLength;
+ memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
+ return UnpackedLength;
}
#ifdef __cplusplus
diff --git a/gwp_asan/crash_handler.h b/gwp_asan/crash_handler.h
index 631c319..4a95069 100644
--- a/gwp_asan/crash_handler.h
+++ b/gwp_asan/crash_handler.h
@@ -1,4 +1,4 @@
-//===-- crash_handler_interface.h -------------------------------*- C++ -*-===//
+//===-- crash_handler.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.
diff --git a/gwp_asan/definitions.h b/gwp_asan/definitions.h
index 563c408..bec0290 100644
--- a/gwp_asan/definitions.h
+++ b/gwp_asan/definitions.h
@@ -1,4 +1,4 @@
-//===-- gwp_asan_definitions.h ----------------------------------*- C++ -*-===//
+//===-- 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.
diff --git a/gwp_asan/guarded_pool_allocator.cpp b/gwp_asan/guarded_pool_allocator.cpp
index b2602e4..a895032 100644
--- a/gwp_asan/guarded_pool_allocator.cpp
+++ b/gwp_asan/guarded_pool_allocator.cpp
@@ -10,7 +10,6 @@
#include "gwp_asan/optional/segv_handler.h"
#include "gwp_asan/options.h"
-#include "gwp_asan/random.h"
#include "gwp_asan/utilities.h"
// RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
@@ -39,14 +38,13 @@
// init-order-fiasco.
GuardedPoolAllocator *SingletonPtr = nullptr;
-class ScopedBoolean {
-public:
- ScopedBoolean(bool &B) : Bool(B) { Bool = true; }
- ~ScopedBoolean() { Bool = false; }
+size_t roundUpTo(size_t Size, size_t Boundary) {
+ return (Size + Boundary - 1) & ~(Boundary - 1);
+}
-private:
- bool &Bool;
-};
+uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) {
+ return Ptr & ~(PageSize - 1);
+}
} // anonymous namespace
// Gets the singleton implementation of this class. Thread-compatible until
@@ -64,7 +62,7 @@
return;
Check(Opts.SampleRate >= 0, "GWP-ASan Error: SampleRate is < 0.");
- Check(Opts.SampleRate <= INT32_MAX, "GWP-ASan Error: SampleRate is > 2^31.");
+ Check(Opts.SampleRate < (1 << 30), "GWP-ASan Error: SampleRate is >= 2^30.");
Check(Opts.MaxSimultaneousAllocations >= 0,
"GWP-ASan Error: MaxSimultaneousAllocations is < 0.");
@@ -73,25 +71,29 @@
State.MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
- State.PageSize = getPlatformPageSize();
+ const size_t PageSize = getPlatformPageSize();
+ // getPageAddr() and roundUpTo() assume the page size to be a power of 2.
+ assert((PageSize & (PageSize - 1)) == 0);
+ State.PageSize = PageSize;
PerfectlyRightAlign = Opts.PerfectlyRightAlign;
size_t PoolBytesRequired =
- State.PageSize * (1 + State.MaxSimultaneousAllocations) +
+ PageSize * (1 + State.MaxSimultaneousAllocations) +
State.MaxSimultaneousAllocations * State.maximumAllocationSize();
- void *GuardedPoolMemory = mapMemory(PoolBytesRequired, kGwpAsanGuardPageName);
+ assert(PoolBytesRequired % PageSize == 0);
+ void *GuardedPoolMemory = reserveGuardedPool(PoolBytesRequired);
- size_t BytesRequired = State.MaxSimultaneousAllocations * sizeof(*Metadata);
+ size_t BytesRequired =
+ roundUpTo(State.MaxSimultaneousAllocations * sizeof(*Metadata), PageSize);
Metadata = reinterpret_cast<AllocationMetadata *>(
- mapMemory(BytesRequired, kGwpAsanMetadataName));
- markReadWrite(Metadata, BytesRequired, kGwpAsanMetadataName);
+ map(BytesRequired, kGwpAsanMetadataName));
// Allocate memory and set up the free pages queue.
- BytesRequired = State.MaxSimultaneousAllocations * sizeof(*FreeSlots);
- FreeSlots = reinterpret_cast<size_t *>(
- mapMemory(BytesRequired, kGwpAsanFreeSlotsName));
- markReadWrite(FreeSlots, BytesRequired, kGwpAsanFreeSlotsName);
+ BytesRequired = roundUpTo(
+ State.MaxSimultaneousAllocations * sizeof(*FreeSlots), PageSize);
+ FreeSlots =
+ reinterpret_cast<size_t *>(map(BytesRequired, kGwpAsanFreeSlotsName));
// Multiply the sample rate by 2 to give a good, fast approximation for (1 /
// SampleRate) chance of sampling.
@@ -101,8 +103,9 @@
AdjustedSampleRatePlusOne = 2;
initPRNG();
- ThreadLocals.NextSampleCounter =
- (getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1;
+ getThreadLocals()->NextSampleCounter =
+ ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) &
+ ThreadLocalPackedVariables::NextSampleCounterMask;
State.GuardedPagePool = reinterpret_cast<uintptr_t>(GuardedPoolMemory);
State.GuardedPagePoolEnd =
@@ -129,39 +132,38 @@
void GuardedPoolAllocator::uninitTestOnly() {
if (State.GuardedPagePool) {
- unmapMemory(reinterpret_cast<void *>(State.GuardedPagePool),
- State.GuardedPagePoolEnd - State.GuardedPagePool,
- kGwpAsanGuardPageName);
+ unreserveGuardedPool();
State.GuardedPagePool = 0;
State.GuardedPagePoolEnd = 0;
}
if (Metadata) {
- unmapMemory(Metadata, State.MaxSimultaneousAllocations * sizeof(*Metadata),
- kGwpAsanMetadataName);
+ unmap(Metadata,
+ roundUpTo(State.MaxSimultaneousAllocations * sizeof(*Metadata),
+ State.PageSize));
Metadata = nullptr;
}
if (FreeSlots) {
- unmapMemory(FreeSlots,
- State.MaxSimultaneousAllocations * sizeof(*FreeSlots),
- kGwpAsanFreeSlotsName);
+ unmap(FreeSlots,
+ roundUpTo(State.MaxSimultaneousAllocations * sizeof(*FreeSlots),
+ State.PageSize));
FreeSlots = nullptr;
}
}
-static uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) {
- return Ptr & ~(PageSize - 1);
-}
-
void *GuardedPoolAllocator::allocate(size_t Size) {
// GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall
// back to the supporting allocator.
- if (State.GuardedPagePoolEnd == 0)
+ if (State.GuardedPagePoolEnd == 0) {
+ getThreadLocals()->NextSampleCounter =
+ (AdjustedSampleRatePlusOne - 1) &
+ ThreadLocalPackedVariables::NextSampleCounterMask;
return nullptr;
+ }
// Protect against recursivity.
- if (ThreadLocals.RecursiveGuard)
+ if (getThreadLocals()->RecursiveGuard)
return nullptr;
- ScopedBoolean SB(ThreadLocals.RecursiveGuard);
+ ScopedRecursiveGuard SRG;
if (Size == 0 || Size > State.maximumAllocationSize())
return nullptr;
@@ -189,8 +191,9 @@
// 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, State.PageSize)),
- Size, kGwpAsanAliveSlotName);
+ const size_t PageSize = State.PageSize;
+ allocateInGuardedPool(reinterpret_cast<void *>(getPageAddr(Ptr, PageSize)),
+ roundUpTo(Size, PageSize));
Meta->RecordAllocation(Ptr, Size);
Meta->AllocationTrace.RecordBacktrace(Backtrace);
@@ -209,7 +212,7 @@
}
void GuardedPoolAllocator::stop() {
- ThreadLocals.RecursiveGuard = true;
+ getThreadLocals()->RecursiveGuard = true;
PoolMutex.tryLock();
}
@@ -240,14 +243,14 @@
// Ensure that the unwinder is not called if the recursive flag is set,
// otherwise non-reentrant unwinders may deadlock.
- if (!ThreadLocals.RecursiveGuard) {
- ScopedBoolean B(ThreadLocals.RecursiveGuard);
+ if (!getThreadLocals()->RecursiveGuard) {
+ ScopedRecursiveGuard SRG;
Meta->DeallocationTrace.RecordBacktrace(Backtrace);
}
}
- markInaccessible(reinterpret_cast<void *>(SlotStart),
- State.maximumAllocationSize(), kGwpAsanGuardPageName);
+ deallocateInGuardedPool(reinterpret_cast<void *>(SlotStart),
+ State.maximumAllocationSize());
// And finally, lock again to release the slot back into the pool.
ScopedLock L(PoolMutex);
@@ -286,7 +289,12 @@
FreeSlots[FreeSlotsLength++] = SlotIndex;
}
-GWP_ASAN_TLS_INITIAL_EXEC
-GuardedPoolAllocator::ThreadLocalPackedVariables
- GuardedPoolAllocator::ThreadLocals;
+uint32_t GuardedPoolAllocator::getRandomUnsigned32() {
+ uint32_t RandomState = getThreadLocals()->RandomState;
+ RandomState ^= RandomState << 13;
+ RandomState ^= RandomState >> 17;
+ RandomState ^= RandomState << 5;
+ getThreadLocals()->RandomState = RandomState;
+ return RandomState;
+}
} // namespace gwp_asan
diff --git a/gwp_asan/guarded_pool_allocator.h b/gwp_asan/guarded_pool_allocator.h
index ae00506..84ebda1 100644
--- a/gwp_asan/guarded_pool_allocator.h
+++ b/gwp_asan/guarded_pool_allocator.h
@@ -13,7 +13,9 @@
#include "gwp_asan/definitions.h"
#include "gwp_asan/mutex.h"
#include "gwp_asan/options.h"
-#include "gwp_asan/random.h"
+#include "gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h"
+#include "gwp_asan/platform_specific/guarded_pool_allocator_posix.h"
+#include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h"
#include "gwp_asan/stack_trace_compressor.h"
#include <stddef.h>
@@ -37,7 +39,7 @@
// 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(){};
+ constexpr GuardedPoolAllocator() {}
GuardedPoolAllocator(const GuardedPoolAllocator &) = delete;
GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete;
@@ -78,11 +80,12 @@
// class must be valid when zero-initialised, and we wish to sample as
// infrequently as possible when this is the case, hence we underflow to
// UINT32_MAX.
- if (GWP_ASAN_UNLIKELY(ThreadLocals.NextSampleCounter == 0))
- ThreadLocals.NextSampleCounter =
- (getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1;
+ if (GWP_ASAN_UNLIKELY(getThreadLocals()->NextSampleCounter == 0))
+ getThreadLocals()->NextSampleCounter =
+ ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) &
+ ThreadLocalPackedVariables::NextSampleCounterMask;
- return GWP_ASAN_UNLIKELY(--ThreadLocals.NextSampleCounter == 0);
+ return GWP_ASAN_UNLIKELY(--getThreadLocals()->NextSampleCounter == 0);
}
// Returns whether the provided pointer is a current sampled allocation that
@@ -124,15 +127,30 @@
// 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. Each mapping is named on platforms that support it, primarily
- // Android. This name must be a statically allocated string, as the Android
- // kernel uses the string pointer directly.
- void *mapMemory(size_t Size, const char *Name) const;
- void unmapMemory(void *Ptr, size_t Size, const char *Name) const;
- void markReadWrite(void *Ptr, size_t Size, const char *Name) const;
- void markInaccessible(void *Ptr, size_t Size, const char *Name) const;
+ // The pool memory is initially reserved and inaccessible, and RW mappings are
+ // subsequently created and destroyed via allocateInGuardedPool() and
+ // deallocateInGuardedPool(). Each mapping is named on platforms that support
+ // it, primarily Android. This name must be a statically allocated string, as
+ // the Android kernel uses the string pointer directly.
+ void *map(size_t Size, const char *Name) const;
+ void unmap(void *Ptr, size_t Size) const;
+
+ // The pool is managed separately, as some platforms (particularly Fuchsia)
+ // manage virtual memory regions as a chunk where individual pages can still
+ // have separate permissions. These platforms maintain metadata about the
+ // region in order to perform operations. The pool is unique as it's the only
+ // thing in GWP-ASan that treats pages in a single VM region on an individual
+ // basis for page protection.
+ // The pointer returned by reserveGuardedPool() is the reserved address range
+ // of (at least) Size bytes.
+ void *reserveGuardedPool(size_t Size);
+ // allocateInGuardedPool() Ptr and Size must be a subrange of the previously
+ // reserved pool range.
+ void allocateInGuardedPool(void *Ptr, size_t Size) const;
+ // deallocateInGuardedPool() Ptr and Size must be an exact pair previously
+ // passed to allocateInGuardedPool().
+ void deallocateInGuardedPool(void *Ptr, size_t Size) const;
+ void unreserveGuardedPool();
// 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.
@@ -191,22 +209,21 @@
// the sample rate.
uint32_t AdjustedSampleRatePlusOne = 0;
- // Pack the thread local variables into a struct to ensure that they're in
- // the same cache line for performance reasons. These are the most touched
- // variables in GWP-ASan.
- struct alignas(8) ThreadLocalPackedVariables {
- constexpr ThreadLocalPackedVariables() {}
- // Thread-local decrementing counter that indicates that a given allocation
- // should be sampled when it reaches zero.
- uint32_t NextSampleCounter = 0;
- // Guard against recursivity. Unwinders often contain complex behaviour that
- // may not be safe for the allocator (i.e. the unwinder calls dlopen(),
- // which calls malloc()). When recursive behaviour is detected, we will
- // automatically fall back to the supporting allocator to supply the
- // allocation.
- bool RecursiveGuard = false;
+ // Additional platform specific data structure for the guarded pool mapping.
+ PlatformSpecificMapData GuardedPagePoolPlatformData = {};
+
+ class ScopedRecursiveGuard {
+ public:
+ ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = true; }
+ ~ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = false; }
};
- static GWP_ASAN_TLS_INITIAL_EXEC ThreadLocalPackedVariables ThreadLocals;
+
+ // Initialise the PRNG, platform-specific.
+ void initPRNG();
+
+ // xorshift (32-bit output), extremely fast PRNG that uses arithmetic
+ // operations only. Seeded using platform-specific mechanisms by initPRNG().
+ uint32_t getRandomUnsigned32();
};
} // namespace gwp_asan
diff --git a/gwp_asan/mutex.h b/gwp_asan/mutex.h
index c29df4c..a7214f5 100644
--- a/gwp_asan/mutex.h
+++ b/gwp_asan/mutex.h
@@ -9,14 +9,11 @@
#ifndef GWP_ASAN_MUTEX_H_
#define GWP_ASAN_MUTEX_H_
-#ifdef __unix__
-#include <pthread.h>
-#else
-#error "GWP-ASan is not supported on this platform."
-#endif
+#include "gwp_asan/platform_specific/mutex_fuchsia.h"
+#include "gwp_asan/platform_specific/mutex_posix.h"
namespace gwp_asan {
-class Mutex {
+class Mutex final : PlatformMutex {
public:
constexpr Mutex() = default;
~Mutex() = default;
@@ -28,11 +25,6 @@
bool tryLock();
// Unlock the mutex.
void unlock();
-
-private:
-#ifdef __unix__
- pthread_mutex_t Mu = PTHREAD_MUTEX_INITIALIZER;
-#endif // defined(__unix__)
};
class ScopedLock {
diff --git a/gwp_asan/optional/backtrace_fuchsia.cpp b/gwp_asan/optional/backtrace_fuchsia.cpp
new file mode 100644
index 0000000..ac7ed6c
--- /dev/null
+++ b/gwp_asan/optional/backtrace_fuchsia.cpp
@@ -0,0 +1,22 @@
+//===-- backtrace_fuchsia.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/optional/backtrace.h"
+
+// GWP-ASan on Fuchsia doesn't currently support backtraces.
+
+namespace gwp_asan {
+namespace options {
+Backtrace_t getBacktraceFunction() { return nullptr; }
+crash_handler::PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; }
+} // namespace options
+
+namespace crash_handler {
+SegvBacktrace_t getSegvBacktraceFunction() { return nullptr; }
+} // namespace crash_handler
+} // namespace gwp_asan
diff --git a/gwp_asan/optional/segv_handler.h b/gwp_asan/optional/segv_handler.h
index 0fed4f2..e1d96a6 100644
--- a/gwp_asan/optional/segv_handler.h
+++ b/gwp_asan/optional/segv_handler.h
@@ -1,4 +1,4 @@
-//===-- crash_handler.h -----------------------------------------*- C++ -*-===//
+//===-- segv_handler.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.
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef GWP_ASAN_OPTIONAL_CRASH_HANDLER_H_
-#define GWP_ASAN_OPTIONAL_CRASH_HANDLER_H_
+#ifndef GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_
+#define GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/options.h"
@@ -87,4 +87,4 @@
} // namespace crash_handler
} // namespace gwp_asan
-#endif // GWP_ASAN_OPTIONAL_CRASH_HANDLER_H_
+#endif // GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_
diff --git a/gwp_asan/optional/segv_handler_fuchsia.cpp b/gwp_asan/optional/segv_handler_fuchsia.cpp
new file mode 100644
index 0000000..ec26afa
--- /dev/null
+++ b/gwp_asan/optional/segv_handler_fuchsia.cpp
@@ -0,0 +1,22 @@
+//===-- segv_handler_fuchsia.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/optional/segv_handler.h"
+
+// GWP-ASan on Fuchsia doesn't currently support signal handlers.
+
+namespace gwp_asan {
+namespace crash_handler {
+void installSignalHandlers(gwp_asan::GuardedPoolAllocator * /* GPA */,
+ Printf_t /* Printf */,
+ PrintBacktrace_t /* PrintBacktrace */,
+ SegvBacktrace_t /* SegvBacktrace */) {}
+
+void uninstallSignalHandlers() {}
+} // namespace crash_handler
+} // namespace gwp_asan
diff --git a/gwp_asan/optional/segv_handler_posix.cpp b/gwp_asan/optional/segv_handler_posix.cpp
index 9a80436..8d9f39a 100644
--- a/gwp_asan/optional/segv_handler_posix.cpp
+++ b/gwp_asan/optional/segv_handler_posix.cpp
@@ -1,4 +1,4 @@
-//===-- crash_handler_posix.cpp ---------------------------------*- C++ -*-===//
+//===-- segv_handler_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.
diff --git a/gwp_asan/options.inc b/gwp_asan/options.inc
index 6cdddfb..67dcfae 100644
--- a/gwp_asan/options.inc
+++ b/gwp_asan/options.inc
@@ -10,7 +10,18 @@
#error "Define GWP_ASAN_OPTION prior to including this file!"
#endif
-GWP_ASAN_OPTION(bool, Enabled, true, "Is GWP-ASan enabled? Defaults to true.")
+#ifndef GWP_ASAN_DEFAULT_ENABLED
+#define GWP_ASAN_DEFAULT_ENABLED true
+#endif
+
+#ifndef GWP_ASAN_STRINGIFY
+#define GWP_ASAN_STRINGIFY(S) GWP_ASAN_STRINGIFY_(S)
+#define GWP_ASAN_STRINGIFY_(S) #S
+#endif
+
+GWP_ASAN_OPTION(bool, Enabled, GWP_ASAN_DEFAULT_ENABLED,
+ "Is GWP-ASan enabled? Defaults to " GWP_ASAN_STRINGIFY(
+ GWP_ASAN_DEFAULT_ENABLED) ".")
GWP_ASAN_OPTION(
bool, PerfectlyRightAlign, false,
@@ -29,7 +40,7 @@
GWP_ASAN_OPTION(int, SampleRate, 5000,
"The probability (1 / SampleRate) that an allocation is "
"selected for GWP-ASan sampling. Default is 5000. Sample rates "
- "up to (2^31 - 1) are supported.")
+ "up to (2^30 - 1) are supported.")
// Developer note - This option is not actually processed by GWP-ASan itself. It
// is included here so that a user can specify whether they want signal handlers
diff --git a/gwp_asan/platform_specific/common_fuchsia.cpp b/gwp_asan/platform_specific/common_fuchsia.cpp
new file mode 100644
index 0000000..b469ef8
--- /dev/null
+++ b/gwp_asan/platform_specific/common_fuchsia.cpp
@@ -0,0 +1,15 @@
+//===-- common_fuchsia.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/common.h"
+
+namespace gwp_asan {
+// This is only used for AllocationTrace.ThreadID and allocation traces are not
+// yet supported on Fuchsia.
+uint64_t getThreadID() { return kInvalidThreadID; }
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/common_posix.cpp b/gwp_asan/platform_specific/common_posix.cpp
index e44e629..813882a 100644
--- a/gwp_asan/platform_specific/common_posix.cpp
+++ b/gwp_asan/platform_specific/common_posix.cpp
@@ -1,4 +1,4 @@
-//===-- common_posix.cpp ---------------------------------*- C++ -*-===//
+//===-- common_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.
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.cpp b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.cpp
new file mode 100644
index 0000000..f58d4b1
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.cpp
@@ -0,0 +1,103 @@
+//===-- guarded_pool_allocator_fuchsia.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/utilities.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <zircon/limits.h>
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+
+namespace gwp_asan {
+void GuardedPoolAllocator::initPRNG() {
+ _zx_cprng_draw(&getThreadLocals()->RandomState, sizeof(uint32_t));
+}
+
+void *GuardedPoolAllocator::map(size_t Size, const char *Name) const {
+ assert((Size % State.PageSize) == 0);
+ zx_handle_t Vmo;
+ zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
+ Check(Status == ZX_OK, "Failed to create Vmo");
+ _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, strlen(Name));
+ zx_vaddr_t Addr;
+ Status = _zx_vmar_map(_zx_vmar_root_self(),
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_ALLOW_FAULTS,
+ 0, Vmo, 0, Size, &Addr);
+ Check(Status == ZX_OK, "Vmo mapping failed");
+ _zx_handle_close(Vmo);
+ return reinterpret_cast<void *>(Addr);
+}
+
+void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
+ zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(),
+ reinterpret_cast<zx_vaddr_t>(Ptr), Size);
+ Check(Status == ZX_OK, "Vmo unmapping failed");
+}
+
+void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) {
+ assert((Size % State.PageSize) == 0);
+ zx_vaddr_t Addr;
+ const zx_status_t Status = _zx_vmar_allocate(
+ _zx_vmar_root_self(),
+ ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0,
+ Size, &GuardedPagePoolPlatformData.Vmar, &Addr);
+ Check(Status == ZX_OK, "Failed to reserve guarded pool allocator memory");
+ _zx_object_set_property(GuardedPagePoolPlatformData.Vmar, ZX_PROP_NAME,
+ kGwpAsanGuardPageName, strlen(kGwpAsanGuardPageName));
+ return reinterpret_cast<void *>(Addr);
+}
+
+void GuardedPoolAllocator::unreserveGuardedPool() {
+ const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar;
+ assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self());
+ Check(_zx_vmar_destroy(Vmar) == ZX_OK, "Failed to destroy a vmar");
+ Check(_zx_handle_close(Vmar) == ZX_OK, "Failed to close a vmar");
+ GuardedPagePoolPlatformData.Vmar = ZX_HANDLE_INVALID;
+}
+
+void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
+ zx_handle_t Vmo;
+ zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
+ Check(Status == ZX_OK, "Failed to create vmo");
+ _zx_object_set_property(Vmo, ZX_PROP_NAME, kGwpAsanAliveSlotName,
+ strlen(kGwpAsanAliveSlotName));
+ const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar;
+ assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self());
+ const size_t Offset =
+ reinterpret_cast<uintptr_t>(Ptr) - State.GuardedPagePool;
+ zx_vaddr_t P;
+ Status = _zx_vmar_map(Vmar,
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE |
+ ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC,
+ Offset, Vmo, 0, Size, &P);
+ Check(Status == ZX_OK, "Vmo mapping failed");
+ _zx_handle_close(Vmo);
+}
+
+void GuardedPoolAllocator::deallocateInGuardedPool(void *Ptr,
+ size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
+ const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar;
+ assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self());
+ const zx_status_t Status =
+ _zx_vmar_unmap(Vmar, reinterpret_cast<zx_vaddr_t>(Ptr), Size);
+ Check(Status == ZX_OK, "Vmar unmapping failed");
+}
+
+size_t GuardedPoolAllocator::getPlatformPageSize() { return ZX_PAGE_SIZE; }
+
+void GuardedPoolAllocator::installAtFork() {}
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h
new file mode 100644
index 0000000..fbd7d3a
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h
@@ -0,0 +1,22 @@
+//===-- guarded_pool_allocator_fuchsia.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__Fuchsia__)
+#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_FUCHSIA_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_FUCHSIA_H_
+
+#include <zircon/types.h>
+
+namespace gwp_asan {
+struct PlatformSpecificMapData {
+ zx_handle_t Vmar;
+};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_FUCHSIA_H_
+#endif // defined(__Fuchsia__)
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp b/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
index a8767a4..dad749b 100644
--- a/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
@@ -16,6 +16,7 @@
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
#ifdef ANDROID
@@ -24,6 +25,7 @@
#define PR_SET_VMA_ANON_NAME 0
#endif // ANDROID
+namespace {
void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) {
#ifdef ANDROID
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Mapping, Size, Name);
@@ -31,39 +33,64 @@
// Anonymous mapping names are only supported on Android.
return;
}
+} // anonymous namespace
namespace gwp_asan {
-void *GuardedPoolAllocator::mapMemory(size_t Size, const char *Name) const {
- void *Ptr =
- mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+
+void GuardedPoolAllocator::initPRNG() {
+ getThreadLocals()->RandomState =
+ static_cast<uint32_t>(time(nullptr) + getThreadID());
+}
+
+void *GuardedPoolAllocator::map(size_t Size, const char *Name) const {
+ assert((Size % State.PageSize) == 0);
+ void *Ptr = mmap(nullptr, Size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
Check(Ptr != MAP_FAILED, "Failed to map guarded pool allocator memory");
MaybeSetMappingName(Ptr, Size, Name);
return Ptr;
}
-void GuardedPoolAllocator::unmapMemory(void *Ptr, size_t Size,
- const char *Name) const {
+void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
Check(munmap(Ptr, Size) == 0,
"Failed to unmap guarded pool allocator memory.");
- MaybeSetMappingName(Ptr, Size, Name);
}
-void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size,
- const char *Name) const {
+void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) {
+ assert((Size % State.PageSize) == 0);
+ void *Ptr =
+ mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ Check(Ptr != MAP_FAILED, "Failed to reserve guarded pool allocator memory");
+ MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName);
+ return Ptr;
+}
+
+void GuardedPoolAllocator::unreserveGuardedPool() {
+ unmap(reinterpret_cast<void *>(State.GuardedPagePool),
+ State.GuardedPagePoolEnd - State.GuardedPagePool);
+}
+
+void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
Check(mprotect(Ptr, Size, PROT_READ | PROT_WRITE) == 0,
- "Failed to set guarded pool allocator memory at as RW.");
- MaybeSetMappingName(Ptr, Size, Name);
+ "Failed to allocate in guarded pool allocator memory");
+ MaybeSetMappingName(Ptr, Size, kGwpAsanAliveSlotName);
}
-void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size,
- const char *Name) const {
+void GuardedPoolAllocator::deallocateInGuardedPool(void *Ptr,
+ size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
// 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.
Check(mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1,
0) != MAP_FAILED,
- "Failed to set guarded pool allocator memory as inaccessible.");
- MaybeSetMappingName(Ptr, Size, Name);
+ "Failed to deallocate in guarded pool allocator memory");
+ MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName);
}
size_t GuardedPoolAllocator::getPlatformPageSize() {
@@ -81,5 +108,4 @@
};
pthread_atfork(Disable, Enable, Enable);
}
-
} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_posix.h b/gwp_asan/platform_specific/guarded_pool_allocator_posix.h
new file mode 100644
index 0000000..7f4ba0d
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_posix.h
@@ -0,0 +1,18 @@
+//===-- guarded_pool_allocator_posix.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__unix__)
+#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_POSIX_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_POSIX_H_
+
+namespace gwp_asan {
+struct PlatformSpecificMapData {};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_POSIX_H_
+#endif // defined(__unix__)
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_tls.h b/gwp_asan/platform_specific/guarded_pool_allocator_tls.h
new file mode 100644
index 0000000..3e2055d
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_tls.h
@@ -0,0 +1,55 @@
+//===-- guarded_pool_allocator_tls.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_TLS_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_TLS_H_
+
+#include "gwp_asan/definitions.h"
+
+#include <stdint.h>
+
+namespace gwp_asan {
+// Pack the thread local variables into a struct to ensure that they're in
+// the same cache line for performance reasons. These are the most touched
+// variables in GWP-ASan.
+struct ThreadLocalPackedVariables {
+ constexpr ThreadLocalPackedVariables()
+ : RandomState(0xacd979ce), NextSampleCounter(0), RecursiveGuard(false) {}
+ // Initialised to a magic constant so that an uninitialised GWP-ASan won't
+ // regenerate its sample counter for as long as possible. The xorshift32()
+ // algorithm used below results in getRandomUnsigned32(0xacd979ce) ==
+ // 0xfffffffe.
+ uint32_t RandomState;
+ // Thread-local decrementing counter that indicates that a given allocation
+ // should be sampled when it reaches zero.
+ uint32_t NextSampleCounter : 31;
+ // The mask is needed to silence conversion errors.
+ static const uint32_t NextSampleCounterMask = (1U << 31) - 1;
+ // Guard against recursivity. Unwinders often contain complex behaviour that
+ // may not be safe for the allocator (i.e. the unwinder calls dlopen(),
+ // which calls malloc()). When recursive behaviour is detected, we will
+ // automatically fall back to the supporting allocator to supply the
+ // allocation.
+ bool RecursiveGuard : 1;
+};
+static_assert(sizeof(ThreadLocalPackedVariables) == sizeof(uint64_t),
+ "thread local data does not fit in a uint64_t");
+} // namespace gwp_asan
+
+#ifdef GWP_ASAN_PLATFORM_TLS_HEADER
+#include GWP_ASAN_PLATFORM_TLS_HEADER
+#else
+namespace gwp_asan {
+inline ThreadLocalPackedVariables *getThreadLocals() {
+ alignas(8) static GWP_ASAN_TLS_INITIAL_EXEC ThreadLocalPackedVariables Locals;
+ return &Locals;
+}
+} // namespace gwp_asan
+#endif // GWP_ASAN_PLATFORM_TLS_HEADER
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_TLS_H_
diff --git a/gwp_asan/platform_specific/mutex_fuchsia.cpp b/gwp_asan/platform_specific/mutex_fuchsia.cpp
new file mode 100644
index 0000000..0431a82
--- /dev/null
+++ b/gwp_asan/platform_specific/mutex_fuchsia.cpp
@@ -0,0 +1,21 @@
+//===-- mutex_fuchsia.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/mutex.h"
+
+#include <lib/sync/mutex.h>
+
+namespace gwp_asan {
+void Mutex::lock() __TA_NO_THREAD_SAFETY_ANALYSIS { sync_mutex_lock(&Mu); }
+
+bool Mutex::tryLock() __TA_NO_THREAD_SAFETY_ANALYSIS {
+ return sync_mutex_trylock(&Mu) == ZX_OK;
+}
+
+void Mutex::unlock() __TA_NO_THREAD_SAFETY_ANALYSIS { sync_mutex_unlock(&Mu); }
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/mutex_fuchsia.h b/gwp_asan/platform_specific/mutex_fuchsia.h
new file mode 100644
index 0000000..edfc1a6
--- /dev/null
+++ b/gwp_asan/platform_specific/mutex_fuchsia.h
@@ -0,0 +1,23 @@
+//===-- mutex_fuchsia.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__Fuchsia__)
+#ifndef GWP_ASAN_MUTEX_FUCHSIA_H_
+#define GWP_ASAN_MUTEX_FUCHSIA_H_
+
+#include <lib/sync/mutex.h>
+
+namespace gwp_asan {
+class PlatformMutex {
+protected:
+ sync_mutex_t Mu = {};
+};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_MUTEX_FUCHSIA_H_
+#endif // defined(__Fuchsia__)
diff --git a/gwp_asan/platform_specific/mutex_posix.h b/gwp_asan/platform_specific/mutex_posix.h
new file mode 100644
index 0000000..7f02391
--- /dev/null
+++ b/gwp_asan/platform_specific/mutex_posix.h
@@ -0,0 +1,23 @@
+//===-- mutex_posix.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
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__unix__)
+#ifndef GWP_ASAN_MUTEX_POSIX_H_
+#define GWP_ASAN_MUTEX_POSIX_H_
+
+#include <pthread.h>
+
+namespace gwp_asan {
+class PlatformMutex {
+protected:
+ pthread_mutex_t Mu = PTHREAD_MUTEX_INITIALIZER;
+};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_MUTEX_POSIX_H_
+#endif // defined(__unix__)
diff --git a/gwp_asan/platform_specific/utilities_fuchsia.cpp b/gwp_asan/platform_specific/utilities_fuchsia.cpp
new file mode 100644
index 0000000..bc9d3a4
--- /dev/null
+++ b/gwp_asan/platform_specific/utilities_fuchsia.cpp
@@ -0,0 +1,19 @@
+//===-- utilities_fuchsia.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/utilities.h"
+
+#include <string.h>
+#include <zircon/sanitizer.h>
+
+namespace gwp_asan {
+void die(const char *Message) {
+ __sanitizer_log_write(Message, strlen(Message));
+ __builtin_trap();
+}
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/utilities_posix.cpp b/gwp_asan/platform_specific/utilities_posix.cpp
index 0e60598..7ee7659 100644
--- a/gwp_asan/platform_specific/utilities_posix.cpp
+++ b/gwp_asan/platform_specific/utilities_posix.cpp
@@ -19,72 +19,14 @@
#endif
namespace gwp_asan {
-
+void die(const char *Message) {
#ifdef __BIONIC__
-void Check(bool Condition, const char *Message) {
- if (Condition)
- return;
if (&android_set_abort_message != nullptr)
android_set_abort_message(Message);
abort();
-}
#else // __BIONIC__
-void Check(bool Condition, const char *Message) {
- if (Condition)
- return;
fprintf(stderr, "%s", Message);
__builtin_trap();
-}
#endif // __BIONIC__
-
-// See `bionic/tests/malloc_test.cpp` in the Android source for documentation
-// regarding their alignment guarantees. We always round up to the closest
-// 8-byte window. As GWP-ASan's malloc(X) can always get exactly an X-sized
-// allocation, an allocation that rounds up to 16-bytes will always be given a
-// 16-byte aligned allocation.
-static size_t alignBionic(size_t RealAllocationSize) {
- if (RealAllocationSize % 8 == 0)
- return RealAllocationSize;
- return RealAllocationSize + 8 - (RealAllocationSize % 8);
}
-
-static size_t alignPowerOfTwo(size_t RealAllocationSize) {
- if (RealAllocationSize <= 2)
- return RealAllocationSize;
- if (RealAllocationSize <= 4)
- return 4;
- if (RealAllocationSize <= 8)
- return 8;
- if (RealAllocationSize % 16 == 0)
- return RealAllocationSize;
- return RealAllocationSize + 16 - (RealAllocationSize % 16);
-}
-
-#ifdef __BIONIC__
-static constexpr AlignmentStrategy PlatformDefaultAlignment =
- AlignmentStrategy::BIONIC;
-#else // __BIONIC__
-static constexpr AlignmentStrategy PlatformDefaultAlignment =
- AlignmentStrategy::POWER_OF_TWO;
-#endif // __BIONIC__
-
-size_t rightAlignedAllocationSize(size_t RealAllocationSize,
- AlignmentStrategy Align) {
- assert(RealAllocationSize > 0);
- if (Align == AlignmentStrategy::DEFAULT)
- Align = PlatformDefaultAlignment;
-
- switch (Align) {
- case AlignmentStrategy::BIONIC:
- return alignBionic(RealAllocationSize);
- case AlignmentStrategy::POWER_OF_TWO:
- return alignPowerOfTwo(RealAllocationSize);
- case AlignmentStrategy::PERFECT:
- return RealAllocationSize;
- case AlignmentStrategy::DEFAULT:
- __builtin_unreachable();
- }
- __builtin_unreachable();
-}
-
} // namespace gwp_asan
diff --git a/gwp_asan/random.cpp b/gwp_asan/random.cpp
deleted file mode 100644
index 2180f92..0000000
--- a/gwp_asan/random.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-//===-- random.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/random.h"
-#include "gwp_asan/common.h"
-
-#include <time.h>
-
-// Initialised to a magic constant so that an uninitialised GWP-ASan won't
-// regenerate its sample counter for as long as possible. The xorshift32()
-// algorithm used below results in getRandomUnsigned32(0xff82eb50) ==
-// 0xfffffea4.
-GWP_ASAN_TLS_INITIAL_EXEC uint32_t RandomState = 0xff82eb50;
-
-namespace gwp_asan {
-void initPRNG() {
- RandomState = time(nullptr) + getThreadID();
-}
-
-uint32_t getRandomUnsigned32() {
- RandomState ^= RandomState << 13;
- RandomState ^= RandomState >> 17;
- RandomState ^= RandomState << 5;
- return RandomState;
-}
-} // namespace gwp_asan
diff --git a/gwp_asan/random.h b/gwp_asan/random.h
deleted file mode 100644
index 953b989..0000000
--- a/gwp_asan/random.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//===-- random.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_RANDOM_H_
-#define GWP_ASAN_RANDOM_H_
-
-#include <stdint.h>
-
-namespace gwp_asan {
-// Initialise the PRNG, using time and thread ID as the seed.
-void initPRNG();
-
-// xorshift (32-bit output), extremely fast PRNG that uses arithmetic operations
-// only. Seeded using walltime.
-uint32_t getRandomUnsigned32();
-} // namespace gwp_asan
-
-#endif // GWP_ASAN_RANDOM_H_
diff --git a/gwp_asan/tests/alignment.cpp b/gwp_asan/tests/alignment.cpp
index bf98f1f..2489ff8 100644
--- a/gwp_asan/tests/alignment.cpp
+++ b/gwp_asan/tests/alignment.cpp
@@ -9,6 +9,8 @@
#include "gwp_asan/tests/harness.h"
#include "gwp_asan/utilities.h"
+#include <vector>
+
TEST(AlignmentTest, PowerOfTwo) {
std::vector<std::pair<size_t, size_t>> AskedSizeToAlignedSize = {
{1, 1}, {2, 2}, {3, 4}, {4, 4}, {5, 8}, {7, 8},
diff --git a/gwp_asan/tests/backtrace.cpp b/gwp_asan/tests/backtrace.cpp
index b3d4427..9515065 100644
--- a/gwp_asan/tests/backtrace.cpp
+++ b/gwp_asan/tests/backtrace.cpp
@@ -8,6 +8,7 @@
#include <string>
+#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
#include "gwp_asan/tests/harness.h"
@@ -76,9 +77,46 @@
TEST(Backtrace, ExceedsStorableLength) {
gwp_asan::AllocationMetadata Meta;
Meta.AllocationTrace.RecordBacktrace(
- [](uintptr_t * /* TraceBuffer */, size_t /* Size */) -> size_t {
- return SIZE_MAX; // Wow, that's big!
+ [](uintptr_t *TraceBuffer, size_t Size) -> size_t {
+ // Need to inintialise the elements that will be packed.
+ memset(TraceBuffer, 0u, Size * sizeof(*TraceBuffer));
+
+ // Indicate that there were more frames, and we just didn't have enough
+ // room to store them.
+ return Size * 2;
+ });
+ // Retrieve a frame from the collected backtrace, make sure it works E2E.
+ uintptr_t TraceOutput;
+ EXPECT_EQ(gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect,
+ __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1));
+}
+
+TEST(Backtrace, ExceedsRetrievableAllocLength) {
+ gwp_asan::AllocationMetadata Meta;
+ constexpr size_t kNumFramesToStore = 3u;
+ Meta.AllocationTrace.RecordBacktrace(
+ [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t {
+ memset(TraceBuffer, kNumFramesToStore,
+ kNumFramesToStore * sizeof(*TraceBuffer));
+ return kNumFramesToStore;
});
uintptr_t TraceOutput;
- EXPECT_EQ(1u, __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1));
+ // Ask for one element, get told that there's `kNumFramesToStore` available.
+ EXPECT_EQ(kNumFramesToStore,
+ __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1));
+}
+
+TEST(Backtrace, ExceedsRetrievableDeallocLength) {
+ gwp_asan::AllocationMetadata Meta;
+ constexpr size_t kNumFramesToStore = 3u;
+ Meta.DeallocationTrace.RecordBacktrace(
+ [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t {
+ memset(TraceBuffer, kNumFramesToStore,
+ kNumFramesToStore * sizeof(*TraceBuffer));
+ return kNumFramesToStore;
+ });
+ uintptr_t TraceOutput;
+ // Ask for one element, get told that there's `kNumFramesToStore` available.
+ EXPECT_EQ(kNumFramesToStore,
+ __gwp_asan_get_deallocation_trace(&Meta, &TraceOutput, 1));
}
diff --git a/gwp_asan/tests/compression.cpp b/gwp_asan/tests/compression.cpp
index 7a5894d..2423c86 100644
--- a/gwp_asan/tests/compression.cpp
+++ b/gwp_asan/tests/compression.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
#include "gwp_asan/stack_trace_compressor.h"
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
namespace gwp_asan {
namespace compression {
diff --git a/gwp_asan/tests/crash_handler_api.cpp b/gwp_asan/tests/crash_handler_api.cpp
index 10a014e..cb30636 100644
--- a/gwp_asan/tests/crash_handler_api.cpp
+++ b/gwp_asan/tests/crash_handler_api.cpp
@@ -16,7 +16,7 @@
using AllocationMetadata = gwp_asan::AllocationMetadata;
using AllocatorState = gwp_asan::AllocatorState;
-class CrashHandlerAPITest : public ::testing::Test {
+class CrashHandlerAPITest : public Test {
public:
void SetUp() override { setupState(); }
diff --git a/gwp_asan/tests/driver.cpp b/gwp_asan/tests/driver.cpp
index b402cec..02ab360 100644
--- a/gwp_asan/tests/driver.cpp
+++ b/gwp_asan/tests/driver.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
diff --git a/gwp_asan/tests/harness.cpp b/gwp_asan/tests/harness.cpp
index 77c25ee..e668c73 100644
--- a/gwp_asan/tests/harness.cpp
+++ b/gwp_asan/tests/harness.cpp
@@ -1,4 +1,12 @@
-#include "harness.h"
+//===-- harness.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/tests/harness.h"
namespace gwp_asan {
namespace test {
diff --git a/gwp_asan/tests/harness.h b/gwp_asan/tests/harness.h
index d303b2c..26be4a4 100644
--- a/gwp_asan/tests/harness.h
+++ b/gwp_asan/tests/harness.h
@@ -11,7 +11,13 @@
#include <stdarg.h>
+#if defined(__Fuchsia__)
+#include <zxtest/zxtest.h>
+using Test = ::zxtest::Test;
+#else
#include "gtest/gtest.h"
+using Test = ::testing::Test;
+#endif
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/optional/backtrace.h"
@@ -32,7 +38,7 @@
}; // namespace test
}; // namespace gwp_asan
-class DefaultGuardedPoolAllocator : public ::testing::Test {
+class DefaultGuardedPoolAllocator : public Test {
public:
void SetUp() override {
gwp_asan::options::Options Opts;
@@ -51,7 +57,7 @@
MaxSimultaneousAllocations;
};
-class CustomGuardedPoolAllocator : public ::testing::Test {
+class CustomGuardedPoolAllocator : public Test {
public:
void
InitNumSlots(decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
@@ -74,7 +80,7 @@
MaxSimultaneousAllocations;
};
-class BacktraceGuardedPoolAllocator : public ::testing::Test {
+class BacktraceGuardedPoolAllocator : public Test {
public:
void SetUp() override {
gwp_asan::options::Options Opts;
diff --git a/gwp_asan/tests/iterate.cpp b/gwp_asan/tests/iterate.cpp
index c40df15..2b8635d 100644
--- a/gwp_asan/tests/iterate.cpp
+++ b/gwp_asan/tests/iterate.cpp
@@ -8,6 +8,9 @@
#include "gwp_asan/tests/harness.h"
+#include <set>
+#include <vector>
+
TEST_F(CustomGuardedPoolAllocator, Iterate) {
InitNumSlots(7);
std::vector<std::pair<void *, size_t>> Allocated;
diff --git a/gwp_asan/tests/late_init.cpp b/gwp_asan/tests/late_init.cpp
index c7d62c8..8a94727 100644
--- a/gwp_asan/tests/late_init.cpp
+++ b/gwp_asan/tests/late_init.cpp
@@ -8,7 +8,7 @@
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/options.h"
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
TEST(LateInit, CheckLateInitIsOK) {
gwp_asan::GuardedPoolAllocator GPA;
diff --git a/gwp_asan/tests/mutex_test.cpp b/gwp_asan/tests/mutex_test.cpp
index 5bc53b9..f68619c 100644
--- a/gwp_asan/tests/mutex_test.cpp
+++ b/gwp_asan/tests/mutex_test.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
#include "gwp_asan/mutex.h"
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
#include <atomic>
#include <mutex>
diff --git a/gwp_asan/tests/slot_reuse.cpp b/gwp_asan/tests/slot_reuse.cpp
index ee4b671..f2a77b0 100644
--- a/gwp_asan/tests/slot_reuse.cpp
+++ b/gwp_asan/tests/slot_reuse.cpp
@@ -8,6 +8,8 @@
#include "gwp_asan/tests/harness.h"
+#include <set>
+
void singleByteGoodAllocDealloc(gwp_asan::GuardedPoolAllocator *GPA) {
void *Ptr = GPA->allocate(1);
EXPECT_NE(nullptr, Ptr);
diff --git a/gwp_asan/utilities.cpp b/gwp_asan/utilities.cpp
new file mode 100644
index 0000000..287630f
--- /dev/null
+++ b/gwp_asan/utilities.cpp
@@ -0,0 +1,63 @@
+//===-- utilities.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/utilities.h"
+
+#include <assert.h>
+
+namespace gwp_asan {
+// See `bionic/tests/malloc_test.cpp` in the Android source for documentation
+// regarding their alignment guarantees. We always round up to the closest
+// 8-byte window. As GWP-ASan's malloc(X) can always get exactly an X-sized
+// allocation, an allocation that rounds up to 16-bytes will always be given a
+// 16-byte aligned allocation.
+static size_t alignBionic(size_t RealAllocationSize) {
+ if (RealAllocationSize % 8 == 0)
+ return RealAllocationSize;
+ return RealAllocationSize + 8 - (RealAllocationSize % 8);
+}
+
+static size_t alignPowerOfTwo(size_t RealAllocationSize) {
+ if (RealAllocationSize <= 2)
+ return RealAllocationSize;
+ if (RealAllocationSize <= 4)
+ return 4;
+ if (RealAllocationSize <= 8)
+ return 8;
+ if (RealAllocationSize % 16 == 0)
+ return RealAllocationSize;
+ return RealAllocationSize + 16 - (RealAllocationSize % 16);
+}
+
+#ifdef __BIONIC__
+static constexpr AlignmentStrategy PlatformDefaultAlignment =
+ AlignmentStrategy::BIONIC;
+#else // __BIONIC__
+static constexpr AlignmentStrategy PlatformDefaultAlignment =
+ AlignmentStrategy::POWER_OF_TWO;
+#endif // __BIONIC__
+
+size_t rightAlignedAllocationSize(size_t RealAllocationSize,
+ AlignmentStrategy Align) {
+ assert(RealAllocationSize > 0);
+ if (Align == AlignmentStrategy::DEFAULT)
+ Align = PlatformDefaultAlignment;
+
+ switch (Align) {
+ case AlignmentStrategy::BIONIC:
+ return alignBionic(RealAllocationSize);
+ case AlignmentStrategy::POWER_OF_TWO:
+ return alignPowerOfTwo(RealAllocationSize);
+ case AlignmentStrategy::PERFECT:
+ return RealAllocationSize;
+ case AlignmentStrategy::DEFAULT:
+ __builtin_unreachable();
+ }
+ __builtin_unreachable();
+}
+} // namespace gwp_asan
diff --git a/gwp_asan/utilities.h b/gwp_asan/utilities.h
index 71d525f..cee5672 100644
--- a/gwp_asan/utilities.h
+++ b/gwp_asan/utilities.h
@@ -6,15 +6,23 @@
//
//===----------------------------------------------------------------------===//
+#ifndef GWP_ASAN_UTILITIES_H_
+#define GWP_ASAN_UTILITIES_H_
+
#include "gwp_asan/definitions.h"
#include <stddef.h>
-#include <stdint.h>
namespace gwp_asan {
-// Checks that `Condition` is true, otherwise fails in a platform-specific way
-// with `Message`.
-void Check(bool Condition, const char *Message);
+// Terminates in a platform-specific way with `Message`.
+void die(const char *Message);
+
+// Checks that `Condition` is true, otherwise dies with `Message`.
+GWP_ASAN_ALWAYS_INLINE void Check(bool Condition, const char *Message) {
+ if (Condition)
+ return;
+ die(Message);
+}
enum class AlignmentStrategy {
// Default => POWER_OF_TWO on most platforms, BIONIC for Android Bionic.
@@ -29,3 +37,5 @@
size_t RealAllocationSize,
AlignmentStrategy Align = AlignmentStrategy::DEFAULT);
} // namespace gwp_asan
+
+#endif // GWP_ASAN_UTILITIES_H_