Merge "Add "product_available" to product available modules"
diff --git a/Android.bp b/Android.bp
index c36a011..e24e7ea 100644
--- a/Android.bp
+++ b/Android.bp
@@ -80,8 +80,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_