diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -279,6 +279,11 @@
 option(COMPILER_RT_USE_BUILTINS_LIBRARY
   "Use compiler-rt builtins instead of libgcc" ${DEFAULT_COMPILER_RT_USE_BUILTINS_LIBRARY})
 
+option(GWP_ASAN_SCUDO_HOOKS
+  "When building scudo, should we install GWP-ASan hooks?"
+  COMPILER_RT_HAS_GWP_ASAN)
+pythonize_bool(GWP_ASAN_SCUDO_HOOKS)
+
 include(config-ix)
 
 #================================
diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake
--- a/compiler-rt/cmake/config-ix.cmake
+++ b/compiler-rt/cmake/config-ix.cmake
@@ -238,6 +238,7 @@
   set(ALL_FUZZER_SUPPORTED_ARCH ${X86_64} ${ARM64})
 endif()
 
+set(ALL_GWP_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${ARM32})
 if(APPLE)
   set(ALL_LSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${MIPS64} ${ARM64})
 else()
@@ -437,6 +438,9 @@
   list_intersect(DFSAN_SUPPORTED_ARCH
     ALL_DFSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
+  list_intersect(GWP_ASAN_SUPPORTED_ARCH
+    ALL_GWP_ASAN_SUPPORTED_ARCH
+    SANITIZER_COMMON_SUPPORTED_ARCH)
   list_intersect(LSAN_SUPPORTED_ARCH
     ALL_LSAN_SUPPORTED_ARCH
     SANITIZER_COMMON_SUPPORTED_ARCH)
@@ -505,6 +509,7 @@
   filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH})
   filter_available_targets(SHADOWCALLSTACK_SUPPORTED_ARCH
     ${ALL_SHADOWCALLSTACK_SUPPORTED_ARCH})
+  filter_available_targets(GWP_ASAN_SUPPORTED_ARCH ${ALL_GWP_ASAN_SUPPORTED_ARCH})
 endif()
 
 if (MSVC)
@@ -532,7 +537,7 @@
   set(OS_NAME "${CMAKE_SYSTEM_NAME}")
 endif()
 
-set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal)
+set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal;gwp_asan)
 set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING
     "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})")
 list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
@@ -676,3 +681,12 @@
 else()
   set(COMPILER_RT_HAS_SHADOWCALLSTACK FALSE)
 endif()
+
+# Note: Fuchsia and Windows are not currently supported by GWP-ASan. Support
+# is planned for these platforms. Darwin is also not supported due to TLS
+# calling malloc on first use.
+if (GWP_ASAN_SUPPORTED_ARCH AND OS_NAME MATCHES "Android|Linux")
+  set(COMPILER_RT_HAS_GWP_ASAN TRUE)
+else()
+  set(COMPILER_RT_HAS_GWP_ASAN FALSE)
+endif()
diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt
@@ -0,0 +1,66 @@
+add_compiler_rt_component(gwp_asan)
+
+include_directories(..)
+
+set(GWP_ASAN_SOURCES
+  guarded_pool_allocator.cpp
+  guarded_pool_allocator_posix.cpp
+  random.cpp
+)
+
+set(GWP_ASAN_HEADERS
+  guarded_pool_allocator.h
+  mutex.h
+  options.h
+  options.inc
+  random.h
+)
+
+# Disable RTTI and exception support, as we want these libraries to be
+# C-compatible. Regular C source files can be linked against the generated
+# GwpAsan libraries using the Clang C compiler.
+set(GWP_ASAN_CFLAGS -fno-rtti -fno-exceptions)
+
+# Options parsing support is optional. GwpAsan is totally independent of
+# sanitizer_common, the options parser is not. This is an optional library
+# that can be used by an allocator to automatically parse GwpAsan options from
+# the environment variable GWP_ASAN_FLAGS, but the allocator can choose to
+# implement its own options parsing and populate the Options struct itself.
+set(GWP_ASAN_OPTIONS_PARSER_SOURCES
+  optional/options_parser.cpp
+)
+set(GWP_ASAN_OPTIONS_PARSER_HEADERS
+  optional/options_parser.h
+)
+set(GWP_ASAN_OPTIONS_PARSER_CFLAGS
+    ${GWP_ASAN_CFLAGS}
+    ${SANITIZER_COMMON_CFLAGS})
+
+if (COMPILER_RT_HAS_GWP_ASAN)
+  foreach(arch ${GWP_ASAN_SUPPORTED_ARCH})
+    add_compiler_rt_runtime(
+      clang_rt.gwp_asan
+      STATIC
+      ARCHS ${arch}
+      SOURCES ${GWP_ASAN_SOURCES}
+      ADDITIONAL_HEADERS ${GWP_ASAN_HEADERS}
+      CFLAGS ${GWP_ASAN_CFLAGS}
+      PARENT_TARGET gwp_asan
+    )
+  endforeach()
+
+  add_compiler_rt_object_libraries(RTGwpAsan
+      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
+      SOURCES ${GWP_ASAN_SOURCES}
+      ADDITIONAL_HEADERS ${GWP_ASAN_HEADERS}
+      CFLAGS ${GWP_ASAN_CFLAGS})
+
+  # Note: If you choose to add this as an object library, ensure you also
+  # include the sanitizer_common flag parsing object lib
+  # 'RTSanitizerCommonNoTermination'.
+  add_compiler_rt_object_libraries(RTGwpAsanOptionsParser
+      ARCHS ${GWP_ASAN_SUPPORTED_ARCH}
+      SOURCES ${GWP_ASAN_OPTIONS_PARSER_SOURCES}
+      ADDITIONAL_HEADERS ${GWP_ASAN_OPTIONS_PARSER_HEADERS}
+      CFLAGS ${GWP_ASAN_OPTIONS_PARSER_CFLAGS})
+endif()
diff --git a/compiler-rt/lib/gwp_asan/definitions.h b/compiler-rt/lib/gwp_asan/definitions.h
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/definitions.h
@@ -0,0 +1,39 @@
+//===-- gwp_asan_definitions.h ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_DEFINITIONS_H_
+#define GWP_ASAN_DEFINITIONS_H_
+
+#define TLS_INITIAL_EXEC thread_local __attribute__((tls_model("initial-exec")))
+
+#ifdef LIKELY
+# undef LIKELY
+#endif // defined(LIKELY)
+#define LIKELY(X) __builtin_expect(!!(X), 1)
+
+#ifdef UNLIKELY
+# undef UNLIKELY
+#endif // defined(UNLIKELY)
+#define UNLIKELY(X) __builtin_expect(!!(X), 0)
+
+#ifdef NORETURN
+# undef NORETURN
+#endif // defined(NORETURN)
+#define NORETURN __attribute__((noreturn))
+
+#ifdef ALWAYS_INLINE
+# undef ALWAYS_INLINE
+#endif // defined(ALWAYS_INLINE)
+#define ALWAYS_INLINE inline __attribute__((always_inline))
+
+#ifdef ALIGNED
+# undef ALIGNED
+#endif // defined(ALIGNED)
+#define ALIGNED(X) __attribute__((aligned(X)))
+
+#endif // GWP_ASAN_DEFINITIONS_H_
diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
@@ -0,0 +1,252 @@
+//===-- guarded_pool_allocator.h --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
+
+#include "gwp_asan/definitions.h"
+#include "gwp_asan/mutex.h"
+#include "gwp_asan/options.h"
+#include "gwp_asan/random.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+namespace gwp_asan {
+// This class is the primary implementation of the allocator portion of GWP-
+// ASan. It is the sole owner of the pool of sequentially allocated guarded
+// slots. It should always be treated as a singleton.
+// This class is **thread-hostile** until init() is completely finished. Any
+// implementing allocator must ensure that init() is called in a thread-safe
+// manner, before any calls to public methods are made.
+class GuardedPoolAllocator {
+public:
+  static constexpr uint64_t kInvalidThreadID = UINT64_MAX;
+  enum class Error {
+    UNKNOWN,
+    USE_AFTER_FREE,
+    DOUBLE_FREE,
+    INVALID_FREE,
+    BUFFER_OVERFLOW,
+    BUFFER_UNDERFLOW
+  };
+
+  struct AllocationMetadata {
+    // Maximum number of stack trace frames to collect for allocations + frees.
+    // TODO(hctim): Implement stack frame compression, a-la Chromium.
+    // Currently the maximum stack frames is one, as we don't collect traces.
+    static constexpr std::size_t kMaximumStackFrames = 1;
+
+    // Records the given allocation metadata into this struct. In the future,
+    // this will collect the allocation trace as well.
+    void RecordAllocation(uintptr_t Addr, std::size_t Size);
+
+    // Record that this allocation is now deallocated. In future, this will
+    // collect the deallocation trace as well.
+    void RecordDeallocation();
+
+    struct CallSiteInfo {
+      // The backtrace to the allocation/deallocation. If the first value is
+      // zero, we did not collect a trace.
+      uintptr_t Trace[kMaximumStackFrames] = {};
+      // The thread ID for this trace, or kInvalidThreadID if not available.
+      uint64_t ThreadID = kInvalidThreadID;
+    };
+
+    // The address of this allocation.
+    uintptr_t Addr = 0;
+    // Represents the actual size of the allocation.
+    std::size_t Size = 0;
+
+    CallSiteInfo AllocationTrace;
+    CallSiteInfo DeallocationTrace;
+
+    // Whether this allocation has been deallocated yet.
+    bool IsDeallocated = false;
+  };
+
+  // During program startup, we must ensure that memory allocations do not land
+  // in this allocation pool if the allocator decides to runtime-disable
+  // GWP-ASan. The constructor value-initialises the class such that if no
+  // further initialisation takes place, calls to shouldSample() and
+  // pointerIsMine() will return false.
+  constexpr GuardedPoolAllocator(){};
+  GuardedPoolAllocator(const GuardedPoolAllocator &) = delete;
+  GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete;
+
+  // Note: This class is expected to be a singleton for the lifetime of the
+  // program. If this object is initialised, it will leak the guarded page pool
+  // and metadata allocations during destruction. We can't clean up these areas
+  // as this may cause a use-after-free on shutdown.
+  ~GuardedPoolAllocator() = default;
+
+  // Initialise the rest of the members of this class. Create the allocation
+  // pool using the provided options. See options.inc for runtime configuration
+  // options.
+  void init(const options::Options &Opts);
+
+  // Return whether the allocation should be randomly chosen for sampling.
+  ALWAYS_INLINE bool shouldSample() {
+    // NextSampleCounter == 0 means we "should regenerate the counter".
+    //                   == 1 means we "should sample this allocation".
+    if (UNLIKELY(NextSampleCounter == 0))
+      NextSampleCounter =
+          (random::getRandomUnsigned64() % AdjustedSampleRate) + 1;
+
+    // GuardedPagePool != 0 if GWP-ASan is enabled.
+    return UNLIKELY(NextSampleCounter-- == 1) && LIKELY(GuardedPagePool != 0);
+  }
+
+  // Returns whether the provided pointer is a current sampled allocation that
+  // is owned by this pool.
+  ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
+    uintptr_t P = reinterpret_cast<uintptr_t>(Ptr);
+    return GuardedPagePool <= P && P < GuardedPagePoolEnd;
+  }
+
+  // Allocate memory in a guarded slot, and return a pointer to the new
+  // allocation. Returns nullptr if the pool is empty, the requested size is too
+  // large for this pool to handle, or the requested size is zero.
+  void *allocate(std::size_t Size);
+
+  // Deallocate memory in a guarded slot. The provided pointer must have been
+  // allocated using this pool. This will set the guarded slot as inaccessible.
+  void deallocate(void *Ptr);
+
+  // Returns the size of the allocation at Ptr.
+  std::size_t getSize(const void *Ptr);
+
+  // Returns the largest allocation that is supported by this pool. Any
+  // allocations larger than this should go to the regular system allocator.
+  std::size_t maximumAllocationSize() const;
+
+  // Dumps an error report (including allocation and deallocation stack traces)
+  // and exits the program. An optional error may be provided if the caller
+  // knows what the error is ahead of time. This is primarily a helper function
+  // to locate the static singleton pointer and call the internal version of
+  // this function. This helps solve the static initialisation order problem.
+  NORETURN static void reportErrorAndDie(uintptr_t AccessPtr,
+                                         Error Error = Error::UNKNOWN);
+
+private:
+  // These functions anonymously map memory or change the permissions of mapped
+  // memory into this process in a platform- specific way. Pointer and size
+  // arguments are expected to be page-aligned. These functions will never
+  // return on error, instead electing to kill the calling process on failure.
+  // Note that memory is initially mapped inaccessible. In order for RW
+  // mappings, call mapMemory() followed by markReadWrite() on the returned
+  // pointer.
+  void *mapMemory(std::size_t Size) const;
+  void markReadWrite(void *Ptr, std::size_t Size) const;
+  void markInaccessible(void *Ptr, std::size_t Size) const;
+
+  // Get the current thread ID, or kInvalidThreadID if failure. Note: This
+  // implementation is platform-specific.
+  static uint64_t getThreadID();
+
+  // Get the page size from the platform-specific implementation. Only needs to
+  // be called once, and the result should be cached in PageSize in this class.
+  static std::size_t getPlatformPageSize();
+
+  // Install the SIGSEGV crash handler for printing use-after-free and heap-
+  // buffer-{under|over}flow exceptions. This is platform specific as even
+  // though POSIX and Windows both support registering handlers through
+  // signal(), we have to use platform-specific signal handlers to obtain the
+  // address that caused the SIGSEGV exception.
+  static void installSignalHandlers();
+
+  // Returns the index of the slot that this pointer resides in. If the pointer
+  // is not owned by this pool, the result is undefined.
+  std::size_t addrToSlot(uintptr_t Ptr) const;
+
+  // Returns the address of to the N-th guarded slot.
+  uintptr_t slotToAddr(std::size_t N) const;
+
+  // Returns a pointer to the metadata for the owned pointer. If the pointer is
+  // not owned by this pool, the result is undefined.
+  AllocationMetadata *addrToMetadata(uintptr_t Ptr) const;
+
+  // Returns the address of the page that this pointer resides in.
+  uintptr_t getPageAddr(uintptr_t Ptr) const;
+
+  // Gets the nearest slot to the provided address.
+  std::size_t getNearestSlot(uintptr_t Ptr) const;
+
+  // Returns whether the provided pointer is a guard page or not. The pointer
+  // must be within memory owned by this pool, else the result is undefined.
+  bool isGuardPage(uintptr_t Ptr) const;
+
+  // Reserve a slot for a new guarded allocation, and place the slot number into
+  // *SlotIndex. Returns false if there are no remaining slots, true otherwise.
+  bool reserveSlot(std::size_t *SlotIndex);
+
+  // Unreserve the guarded slot.
+  void freeSlot(std::size_t SlotIndex);
+
+  // Returns the offset (in bytes) between the start of a guarded slot and where
+  // the start of the allocation should take place. Determined using the size of
+  // the allocation and the options provided at init-time.
+  uintptr_t allocationSlotOffset(std::size_t AllocationSize);
+
+  // Returns the diagnosis for an unknown error. If the diagnosis is not
+  // Error::INVALID_FREE or Error::UNKNOWN, the metadata for the slot
+  // responsible for the error is placed in *Meta.
+  Error diagnoseUnknownError(uintptr_t AccessPtr, AllocationMetadata **Meta);
+
+  NORETURN void reportErrorAndDieInternal(uintptr_t AccessPtr, Error Error);
+
+  // Cached page size for this system in bytes.
+  std::size_t PageSize = 0;
+
+  // A mutex to protect the guarded slot and metadata pool for this class.
+  Mutex PoolMutex;
+  // The number of guarded slots that this pool holds.
+  std::size_t MaxSimultaneousAllocations = 0;
+  // Record the number allocations that we've sampled. We store this amount so
+  // that we don't randomly choose to recycle a slot that previously had an
+  // allocation before all the slots have been utilised.
+  std::size_t NumSampledAllocations = 0;
+  // Pointer to the pool of guarded slots. Note that this points to the start of
+  // the pool (which is a guard page), not a pointer to the first guarded page.
+  uintptr_t GuardedPagePool = UINTPTR_MAX;
+  uintptr_t GuardedPagePoolEnd = 0;
+  // Pointer to the allocation metadata (allocation/deallocation stack traces),
+  // if any.
+  AllocationMetadata *Metadata = nullptr;
+
+  // Pointer to an array of free slot indexes.
+  std::size_t *FreeSlots = nullptr;
+  // The current length of the list of free slots.
+  std::size_t FreeSlotsLength = 0;
+
+  // See options.{h, inc} for more information.
+  bool PerfectlyRightAlign = false;
+
+  // Printf function supplied by the implementing allocator. We can't (in
+  // general) use printf() from the cstdlib as it may malloc(), causing infinite
+  // recursion.
+  options::Printf_t Printf = nullptr;
+
+  // The adjusted sample rate for allocation sampling. Default *must* be
+  // nonzero, as dynamic initialisation may call malloc (e.g. from libstdc++)
+  // before GPA::init() is called. This would cause an error in shouldSample(),
+  // where we would calculate modulo zero. This value is set UINT64_MAX, as when
+  // GWP-ASan is disabled, we wish to never spend wasted cycles recalculating
+  // the sample rate.
+  ALIGNED(8) uint64_t AdjustedSampleRate = UINT64_MAX;
+  // Thread-local decrementing counter that indicates that a given allocation
+  // should be sampled when it reaches zero.
+  static TLS_INITIAL_EXEC uint64_t NextSampleCounter;
+};
+
+static_assert(std::is_trivially_destructible<GuardedPoolAllocator>::value,
+              "GuardedPoolAllocator must be trivially destructible.");
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
@@ -0,0 +1,388 @@
+//===-- guarded_pool_allocator.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/guarded_pool_allocator.h"
+
+#include "gwp_asan/options.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <new>
+
+using AllocationMetadata = gwp_asan::GuardedPoolAllocator::AllocationMetadata;
+using Error = gwp_asan::GuardedPoolAllocator::Error;
+
+namespace gwp_asan {
+namespace {
+// Forward declare the pointer to the singleton version of this class.
+// Instantiated during initialisation, this allows the signal handler
+// to find this class in order to deduce the root cause of failures. Must not be
+// referenced by users outside this translation unit, in order to avoid
+// init-order-fiasco.
+GuardedPoolAllocator *SingletonPtr = nullptr;
+} // anonymous namespace
+
+void GuardedPoolAllocator::AllocationMetadata::RecordAllocation(
+    uintptr_t AllocAddr, std::size_t AllocSize) {
+  Addr = AllocAddr;
+  Size = AllocSize;
+  IsDeallocated = false;
+
+  // TODO(hctim): Implement stack trace collection.
+  AllocationTrace.ThreadID = getThreadID();
+  DeallocationTrace.ThreadID = kInvalidThreadID;
+  AllocationTrace.Trace[0] = 0;
+  DeallocationTrace.Trace[0] = 0;
+}
+
+void GuardedPoolAllocator::AllocationMetadata::RecordDeallocation() {
+  IsDeallocated = true;
+  // TODO(hctim): Implement stack trace collection.
+  DeallocationTrace.ThreadID = getThreadID();
+}
+
+void GuardedPoolAllocator::init(const options::Options &Opts) {
+  // Note: We return from the constructor here if GWP-ASan is not available.
+  // This will stop heap-allocation of class members, as well as mmap() of the
+  // guarded slots.
+  if (!Opts.Enabled || Opts.SampleRate == 0)
+    return;
+
+  // TODO(hctim): Add a death unit test for this.
+  if (SingletonPtr) {
+    (*SingletonPtr->Printf)(
+        "GWP-ASan Error: init() has already been called.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  SingletonPtr = this;
+
+  MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
+
+  PageSize = getPlatformPageSize();
+
+  PerfectlyRightAlign = Opts.PerfectlyRightAlign;
+  Printf = Opts.Printf;
+
+  std::size_t PoolBytesRequired =
+      PageSize * (1 + MaxSimultaneousAllocations) +
+      MaxSimultaneousAllocations * maximumAllocationSize();
+  void *GuardedPoolMemory = mapMemory(PoolBytesRequired);
+
+  // Round up memory mappings to the nearest page size.
+  std::size_t BytesRequired = MaxSimultaneousAllocations * sizeof(*Metadata);
+  Metadata = reinterpret_cast<AllocationMetadata *>(mapMemory(BytesRequired));
+  markReadWrite(Metadata, BytesRequired);
+
+  // Allocate memory and set up the free pages queue.
+  BytesRequired = MaxSimultaneousAllocations * sizeof(*FreeSlots);
+  FreeSlots = reinterpret_cast<std::size_t *>(mapMemory(BytesRequired));
+  markReadWrite(FreeSlots, BytesRequired);
+
+  // Multiply the sample rate by 2 to give a good, fast approximation for (1 /
+  // SampleRate) chance of sampling.
+  if (Opts.SampleRate != 1)
+    AdjustedSampleRate = Opts.SampleRate * 2;
+  else
+    AdjustedSampleRate = 1;
+
+  GuardedPagePool = reinterpret_cast<uintptr_t>(GuardedPoolMemory);
+  GuardedPagePoolEnd =
+      reinterpret_cast<uintptr_t>(GuardedPoolMemory) + PoolBytesRequired;
+
+  // Ensure that signal handlers are installed as late as possible, as the class
+  // is not thread-safe until init() is finished, and thus a SIGSEGV may cause a
+  // race to members if recieved during init().
+  if (Opts.InstallSignalHandlers)
+    installSignalHandlers();
+}
+
+void *GuardedPoolAllocator::allocate(std::size_t Size) {
+  if (Size == 0 || Size > maximumAllocationSize())
+    return nullptr;
+
+  ScopedLock L(&PoolMutex);
+
+  size_t Index;
+  if (!reserveSlot(&Index))
+    return nullptr;
+
+  uintptr_t Ptr = slotToAddr(Index);
+  Ptr += allocationSlotOffset(Size);
+  AllocationMetadata *Meta = addrToMetadata(Ptr);
+
+  // If a slot is multiple pages in size, and the allocation takes up a single
+  // page, we can improve overflow detection by leaving the unused pages as
+  // unmapped.
+  markReadWrite(reinterpret_cast<void *>(getPageAddr(Ptr)), Size);
+
+  Meta->RecordAllocation(Ptr, Size);
+
+  return reinterpret_cast<void *>(Ptr);
+}
+
+void GuardedPoolAllocator::deallocate(void *Ptr) {
+  assert(pointerIsMine(Ptr) && "Pointer is not mine!");
+  uintptr_t UPtr = reinterpret_cast<uintptr_t>(Ptr);
+  uintptr_t SlotStart = slotToAddr(addrToSlot(UPtr));
+  AllocationMetadata *Meta = addrToMetadata(UPtr);
+  if (Meta->Addr != UPtr)
+    reportErrorAndDie(UPtr, Error::INVALID_FREE);
+
+  ScopedLock L(&PoolMutex);
+
+  if (Meta->IsDeallocated)
+    reportErrorAndDie(UPtr, Error::DOUBLE_FREE);
+
+  Meta->RecordDeallocation();
+
+  markInaccessible(reinterpret_cast<void *>(SlotStart),
+                   maximumAllocationSize());
+  freeSlot(addrToSlot(UPtr));
+}
+
+std::size_t GuardedPoolAllocator::getSize(const void *Ptr) {
+  assert(pointerIsMine(Ptr));
+  ScopedLock L(&PoolMutex);
+  AllocationMetadata *Meta = addrToMetadata(reinterpret_cast<uintptr_t>(Ptr));
+  assert(Meta->Addr == reinterpret_cast<uintptr_t>(Ptr));
+  return Meta->Size;
+}
+
+std::size_t GuardedPoolAllocator::maximumAllocationSize() const {
+  return PageSize;
+}
+
+AllocationMetadata *GuardedPoolAllocator::addrToMetadata(uintptr_t Ptr) const {
+  assert(pointerIsMine(reinterpret_cast<void *>(Ptr)));
+  return &Metadata[addrToSlot(Ptr)];
+}
+
+std::size_t GuardedPoolAllocator::addrToSlot(uintptr_t Ptr) const {
+  std::size_t ByteOffsetFromPoolStart = Ptr - GuardedPagePool;
+  return ByteOffsetFromPoolStart / (maximumAllocationSize() + PageSize);
+}
+
+uintptr_t GuardedPoolAllocator::slotToAddr(std::size_t N) const {
+  return GuardedPagePool + (PageSize * (1 + N)) + (maximumAllocationSize() * N);
+}
+
+uintptr_t GuardedPoolAllocator::getPageAddr(uintptr_t Ptr) const {
+  return Ptr & ~(static_cast<uintptr_t>(PageSize) - 1);
+}
+
+bool GuardedPoolAllocator::isGuardPage(uintptr_t Ptr) const {
+  std::size_t PageOffsetFromPoolStart = (Ptr - GuardedPagePool) / PageSize;
+  std::size_t PagesPerSlot = maximumAllocationSize() / PageSize;
+  return (PageOffsetFromPoolStart % (PagesPerSlot + 1)) == 0;
+}
+
+bool GuardedPoolAllocator::reserveSlot(std::size_t *SlotIndex) {
+  // Avoid potential reuse of a slot before we have made at least a single
+  // allocation in each slot. Helps with our use-after-free detection.
+  if (NumSampledAllocations < MaxSimultaneousAllocations) {
+    *SlotIndex = NumSampledAllocations++;
+    return true;
+  }
+
+  if (FreeSlotsLength == 0)
+    return false;
+
+  std::size_t ReservedIndex = random::getRandomUnsigned64() % FreeSlotsLength;
+  *SlotIndex = FreeSlots[ReservedIndex];
+  FreeSlots[ReservedIndex] = FreeSlots[--FreeSlotsLength];
+  return true;
+}
+
+void GuardedPoolAllocator::freeSlot(std::size_t SlotIndex) {
+  assert(FreeSlotsLength < MaxSimultaneousAllocations);
+  FreeSlots[FreeSlotsLength++] = SlotIndex;
+}
+
+uintptr_t GuardedPoolAllocator::allocationSlotOffset(std::size_t Size) {
+  assert(Size > 0);
+
+  bool ShouldRightAlign = random::getRandomUnsigned64() % 2 == 0;
+  if (!ShouldRightAlign)
+    return 0;
+
+  uintptr_t Offset = maximumAllocationSize();
+  if (!PerfectlyRightAlign) {
+    if (Size == 3)
+      Size = 4;
+    else if (Size > 4 && Size <= 8)
+      Size = 8;
+    else if (Size > 8 && (Size % 16) != 0)
+      Size += 16 - (Size % 16);
+  }
+  Offset -= Size;
+  return Offset;
+}
+
+NORETURN void GuardedPoolAllocator::reportErrorAndDie(uintptr_t AccessPtr,
+                                                      Error Error) {
+  assert(SingletonPtr != nullptr);
+  SingletonPtr->reportErrorAndDieInternal(AccessPtr, Error);
+}
+
+std::size_t GuardedPoolAllocator::getNearestSlot(uintptr_t Ptr) const {
+  if (Ptr <= GuardedPagePool + PageSize)
+    return 0;
+  if (Ptr > GuardedPagePoolEnd - PageSize)
+    return MaxSimultaneousAllocations - 1;
+
+  if (!isGuardPage(Ptr))
+    return addrToSlot(Ptr);
+
+  if (Ptr % PageSize <= PageSize / 2)
+    return addrToSlot(Ptr - PageSize); // Round down.
+  return addrToSlot(Ptr + PageSize);   // Round up.
+}
+
+Error GuardedPoolAllocator::diagnoseUnknownError(uintptr_t AccessPtr,
+                                                 AllocationMetadata **Meta) {
+  // Let's try and figure out what the source of this error is.
+  if (isGuardPage(AccessPtr)) {
+    std::size_t Slot = getNearestSlot(AccessPtr);
+    AllocationMetadata *SlotMeta = addrToMetadata(slotToAddr(Slot));
+
+    // Ensure that this slot was allocated once upon a time.
+    if (!SlotMeta->Addr)
+      return Error::UNKNOWN;
+    *Meta = SlotMeta;
+
+    if (SlotMeta->Addr < AccessPtr)
+      return Error::BUFFER_OVERFLOW;
+    else
+      return Error::BUFFER_UNDERFLOW;
+  }
+
+  // Access wasn't a guard page, check for use-after-free.
+  AllocationMetadata *SlotMeta = addrToMetadata(AccessPtr);
+  if (SlotMeta->IsDeallocated) {
+    *Meta = SlotMeta;
+    return Error::USE_AFTER_FREE;
+  }
+
+  // If we have reached here, the error is still unknown. There is no metadata
+  // available.
+  return Error::UNKNOWN;
+}
+
+void printErrorTypeAndMaybeDie(Error Error, uintptr_t AccessPtr,
+                               AllocationMetadata *Meta,
+                               options::Printf_t Printf) {
+  switch (Error) {
+  case Error::UNKNOWN:
+    Printf("GWP-ASan couldn't automatically determine the source of the "
+           "memory error when accessing 0x%zx. It was likely caused by a wild "
+           "memory access into the GWP-ASan pool.\n",
+           AccessPtr);
+    exit(EXIT_FAILURE);
+  case Error::USE_AFTER_FREE:
+    Printf("Use after free occurred when accessing memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  case Error::DOUBLE_FREE:
+    Printf("Double free occurred when trying to free memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  case Error::INVALID_FREE:
+    Printf(
+        "Invalid (wild) free occurred when trying to free memory at: 0x%zx\n",
+        AccessPtr);
+    // It's possible for an invalid free to fall onto a slot that has never been
+    // allocated. If this is the case, there is no valid metadata.
+    if (Meta == nullptr)
+      exit(EXIT_FAILURE);
+    break;
+  case Error::BUFFER_OVERFLOW:
+    Printf("Buffer overflow occurred when accessing memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  case Error::BUFFER_UNDERFLOW:
+    Printf("Buffer underflow occurred when accessing memory at: 0x%zx\n",
+           AccessPtr);
+    break;
+  }
+
+  Printf("0x%zx is ", AccessPtr);
+  if (AccessPtr < Meta->Addr)
+    Printf("located %zu bytes to the left of ", Meta->Addr - AccessPtr);
+  else if (AccessPtr > Meta->Addr)
+    Printf("located %zu bytes to the right of ", AccessPtr - Meta->Addr);
+  Printf("a %zu-byte allocation located at 0x%zx\n", Meta->Size, Meta->Addr);
+}
+
+void printThreadInformation(Error Error, uintptr_t AccessPtr,
+                            AllocationMetadata *Meta,
+                            options::Printf_t Printf) {
+  Printf("0x%zx was allocated by thread ", AccessPtr);
+  if (Meta->AllocationTrace.ThreadID == UINT64_MAX)
+    Printf("UNKNOWN.\n");
+  else
+    Printf("%zu.\n", Meta->AllocationTrace.ThreadID);
+
+  if (Error == Error::USE_AFTER_FREE || Error == Error::DOUBLE_FREE) {
+    Printf("0x%zx was freed by thread ", AccessPtr);
+    if (Meta->AllocationTrace.ThreadID == UINT64_MAX)
+      Printf("UNKNOWN.\n");
+    else
+      Printf("%zu.\n", Meta->AllocationTrace.ThreadID);
+  }
+}
+
+NORETURN void
+GuardedPoolAllocator::reportErrorAndDieInternal(uintptr_t AccessPtr,
+                                                Error Error) {
+  if (!pointerIsMine(reinterpret_cast<void *>(AccessPtr))) {
+    Printf("Segmentation fault\n");
+    exit(EXIT_FAILURE);
+  }
+
+  // Attempt to prevent races to re-use the same slot that triggered this error.
+  // This does not guarantee that there are no races, because another thread can
+  // take the locks during the time that the signal handler is being called.
+  PoolMutex.tryLock();
+
+  Printf("*** GWP-ASan detected a memory error ***\n");
+
+  AllocationMetadata *Meta = nullptr;
+
+  if (Error == Error::UNKNOWN) {
+    Error = diagnoseUnknownError(AccessPtr, &Meta);
+  } else {
+    std::size_t Slot = getNearestSlot(AccessPtr);
+    Meta = addrToMetadata(slotToAddr(Slot));
+    // Ensure that this slot has been previously allocated.
+    if (!Meta->Addr)
+      Meta = nullptr;
+  }
+
+  // Print the error information, and if there is no valid metadata, die here.
+  printErrorTypeAndMaybeDie(Error, AccessPtr, Meta, Printf);
+
+  // Ensure that we have a valid metadata pointer from this point forward.
+  if (Meta == nullptr) {
+    Printf("GWP-ASan internal unreachable error. Metadata is not null.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  printThreadInformation(Error, AccessPtr, Meta, Printf);
+  // TODO(hctim): Implement stack unwinding here. Ask the caller to provide us
+  // with the base pointer, and we unwind the stack to give a stack trace for
+  // the access.
+  // TODO(hctim): Implement dumping here of allocation/deallocation traces.
+
+  exit(EXIT_FAILURE);
+}
+
+TLS_INITIAL_EXEC uint64_t GuardedPoolAllocator::NextSampleCounter = 0;
+} // namespace gwp_asan
diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator_posix.cpp b/compiler-rt/lib/gwp_asan/guarded_pool_allocator_posix.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator_posix.cpp
@@ -0,0 +1,102 @@
+//===-- guarded_pool_allocator_posix.cpp ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/guarded_pool_allocator.h"
+
+#include <cstdlib>
+#include <errno.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace gwp_asan {
+
+void *GuardedPoolAllocator::mapMemory(size_t Size) const {
+  void *Ptr =
+      mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+
+  if (Ptr == MAP_FAILED) {
+    Printf("Failed to map guarded pool allocator memory, errno: %d\n", errno);
+    Printf("  mmap(nullptr, %zu, ...) failed.\n", Size);
+    exit(EXIT_FAILURE);
+  }
+  return Ptr;
+}
+
+void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size) const {
+  if (mprotect(Ptr, Size, PROT_READ | PROT_WRITE) != 0) {
+    Printf("Failed to set guarded pool allocator memory at as RW, errno: %d\n",
+           errno);
+    Printf("  mprotect(%p, %zu, RW) failed.\n", Ptr, Size);
+    exit(EXIT_FAILURE);
+  }
+}
+
+void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size) const {
+  // mmap() a PROT_NONE page over the address to release it to the system, if
+  // we used mprotect() here the system would count pages in the quarantine
+  // against the RSS.
+  if (mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1,
+           0) == MAP_FAILED) {
+    Printf("Failed to set guarded pool allocator memory as inaccessible, "
+           "errno: %d\n",
+           errno);
+    Printf("  mmap(%p, %zu, NONE, ...) failed.\n", Ptr, Size);
+    exit(EXIT_FAILURE);
+  }
+}
+
+std::size_t GuardedPoolAllocator::getPlatformPageSize() {
+  return sysconf(_SC_PAGESIZE);
+}
+
+void (*PreviousSignumHandler)(int) = nullptr;
+void (*PreviousSigactionHandler)(int, siginfo_t *, void *) = nullptr;
+
+static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
+  if (sig != SIGSEGV)
+    return;
+
+  if (info == nullptr)
+    return;
+
+  gwp_asan::GuardedPoolAllocator::reportErrorAndDie(
+      reinterpret_cast<uintptr_t>(info->si_addr));
+
+  if (PreviousSignumHandler)
+    PreviousSignumHandler(sig);
+  if (PreviousSigactionHandler)
+    PreviousSigactionHandler(sig, info, ucontext);
+}
+
+void GuardedPoolAllocator::installSignalHandlers() {
+  struct sigaction OldHandler;
+  if (sigaction(SIGSEGV, nullptr, &OldHandler) == 0) {
+    if (OldHandler.sa_flags & SA_SIGINFO)
+      PreviousSigactionHandler = OldHandler.sa_sigaction;
+    else
+      PreviousSignumHandler = OldHandler.sa_handler;
+  }
+
+  struct sigaction Action;
+  Action.sa_sigaction = sigSegvHandler;
+  Action.sa_flags = SA_SIGINFO;
+  sigaction(SIGSEGV, &Action, nullptr);
+}
+
+uint64_t GuardedPoolAllocator::getThreadID() {
+#ifdef SYS_gettid
+  return syscall(SYS_gettid);
+#else
+  return kInvalidThreadID;
+#endif
+}
+
+} // namespace gwp_asan
diff --git a/compiler-rt/lib/gwp_asan/mutex.h b/compiler-rt/lib/gwp_asan/mutex.h
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/mutex.h
@@ -0,0 +1,94 @@
+//===-- mutex.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
+//
+//===----------------------------------------------------------------------===//
+
+// TODO(hctim): Move this implementation and Scudo's implementation into a
+// unified base implementation. We shouldn't need to have separate
+// implementations for this.
+
+#ifndef GWP_ASAN_MUTEX_H_
+#define GWP_ASAN_MUTEX_H_
+
+#include "gwp_asan/definitions.h"
+
+#include <cstdint>
+
+#ifdef __unix__
+#include <sched.h>
+#endif // defined(__unix__)
+
+namespace gwp_asan {
+class Mutex {
+public:
+  void lock();
+  bool tryLock();
+  void unlock();
+
+private:
+  void yieldProcessor(uint8_t Count);
+  void yieldPlatform();
+
+  void lockSlow();
+
+  bool Locked = false;
+};
+
+class ScopedLock {
+public:
+  explicit ScopedLock(Mutex *Mx) : Mu(Mx) { Mu->lock(); }
+  ~ScopedLock() { Mu->unlock(); }
+
+private:
+  Mutex *Mu;
+};
+
+ALWAYS_INLINE void Mutex::lock() {
+  if (tryLock())
+    return;
+  lockSlow();
+}
+
+ALWAYS_INLINE bool Mutex::tryLock() {
+  return !__atomic_exchange_n(&Locked, true, __ATOMIC_ACQUIRE);
+}
+
+ALWAYS_INLINE void Mutex::unlock() {
+  __atomic_store_n(&Locked, false, __ATOMIC_RELEASE);
+}
+
+ALWAYS_INLINE void Mutex::yieldProcessor(uint8_t Count) {
+#if defined(__i386__) || defined(__x86_64__)
+  asm volatile("" ::: "memory");
+  for (uint8_t i = 0; i < Count; ++i)
+    asm volatile("pause");
+#elif defined(__aarch64__) || defined(__arm__)
+  asm volatile("" ::: "memory");
+  for (uint8_t i = 0; I < Count; ++i)
+    asm volatile("yield");
+#endif
+  asm volatile("" ::: "memory");
+}
+
+ALWAYS_INLINE void Mutex::lockSlow() {
+  for (uint32_t i = 0;; ++i) {
+    if (i < 10)
+      yieldProcessor(10);
+    else
+      yieldPlatform();
+
+    if (!__atomic_load_n(&Locked, __ATOMIC_RELAXED) && tryLock())
+      return;
+  }
+}
+
+#ifdef __unix__
+ALWAYS_INLINE void Mutex::yieldPlatform() { sched_yield(); }
+#endif // defined(__unix__)
+
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_MUTEX_H_
diff --git a/compiler-rt/lib/gwp_asan/optional/options_parser.h b/compiler-rt/lib/gwp_asan/optional/options_parser.h
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/optional/options_parser.h
@@ -0,0 +1,35 @@
+//===-- options_parser.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_OPTIONAL_OPTIONS_PARSER_H_
+#define GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
+
+// Include sanitizer_common first as gwp_asan/options.h transiently includes
+// definitions.h, which overwrites some of the definitions in sanitizer_common.
+#include "sanitizer_common/sanitizer_common.h"
+
+#include "gwp_asan/options.h"
+
+namespace gwp_asan {
+namespace options {
+
+// Parse the options from the GWP_ASAN_FLAGS environment variable.
+void initOptions();
+// Returns a pointer to the initialised options. Call initOptions() prior to
+// calling this function.
+Options *getOptions();
+
+} // namespace options
+} // namespace gwp_asan
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *
+__gwp_asan_default_options();
+}
+
+#endif // GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
diff --git a/compiler-rt/lib/gwp_asan/optional/options_parser.cpp b/compiler-rt/lib/gwp_asan/optional/options_parser.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/optional/options_parser.cpp
@@ -0,0 +1,86 @@
+//===-- options_parser.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/options_parser.h"
+
+#include <cstdarg>
+#include <cstdlib>
+#include <cstring>
+
+#include "gwp_asan/options.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+
+namespace gwp_asan {
+namespace options {
+void registerGwpAsanFlags(__sanitizer::FlagParser *parser, Options *o) {
+#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
+  RegisterFlag(parser, #Name, Description, &o->Name);
+#include "gwp_asan/options.inc"
+#undef GWP_ASAN_OPTION
+}
+
+const char *getCompileDefinitionGwpAsanDefaultOptions() {
+#ifdef GWP_ASAN_DEFAULT_OPTIONS
+  return SANITIZER_STRINGIFY(GWP_ASAN_DEFAULT_OPTIONS);
+#else
+  return "";
+#endif
+}
+
+const char *getGwpAsanDefaultOptions() {
+  return (__gwp_asan_default_options) ? __gwp_asan_default_options() : "";
+}
+
+void initOptions() {
+  Options *o = getOptions();
+  o->setDefaults();
+
+  __sanitizer::FlagParser Parser;
+  registerGwpAsanFlags(&Parser, o);
+
+  // Override from compile definition.
+  Parser.ParseString(getCompileDefinitionGwpAsanDefaultOptions());
+
+  // Override from user-specified string.
+  Parser.ParseString(getGwpAsanDefaultOptions());
+
+  // Override from environment.
+  Parser.ParseString(__sanitizer::GetEnv("GWP_ASAN_OPTIONS"));
+
+  __sanitizer::InitializeCommonFlags();
+
+  // Sanity checks and default settings for the parameters.
+  if (o->Enabled && o->MaxSimultaneousAllocations <= 0) {
+    __sanitizer::Printf(
+        "GWP-ASan ERROR: MaxSimultaneousAllocations must be >= 0 when "
+        "GWP-ASan is enabled.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  if (o->Enabled && o->SampleRate < 1) {
+    __sanitizer::Printf("GWP-ASan ERROR: SampleRate must be >= 1 when "
+                        "GWP-ASan is enabled.\n");
+    exit(EXIT_FAILURE);
+  }
+
+  o->Printf = __sanitizer::Printf;
+}
+
+Options *getOptions() {
+  static Options GwpAsanFlags;
+  return &GwpAsanFlags;
+}
+
+} // namespace options
+} // namespace gwp_asan
+
+SANITIZER_INTERFACE_WEAK_DEF(const char *, __gwp_asan_default_options, void) {
+  return "";
+}
diff --git a/compiler-rt/lib/gwp_asan/options.h b/compiler-rt/lib/gwp_asan/options.h
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/options.h
@@ -0,0 +1,41 @@
+//===-- options.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_OPTIONS_H_
+#define GWP_ASAN_OPTIONS_H_
+
+namespace gwp_asan {
+namespace options {
+// The function pointer type for printf(). Follows the standard format from the
+// sanitizers library. If the supported allocator exposes printing via a
+// different function signature, please provide a wrapper which has this
+// printf() signature, and pass the wrapper instead.
+typedef void (*Printf_t)(const char *Format, ...);
+
+struct Options {
+  Printf_t Printf = nullptr;
+
+  // Read the options from the included definitions file.
+#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
+  Type Name = DefaultValue;
+#include "gwp_asan/options.inc"
+#undef GWP_ASAN_OPTION
+
+  void setDefaults() {
+#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description)                 \
+  Name = DefaultValue;
+#include "gwp_asan/options.inc"
+#undef GWP_ASAN_OPTION
+
+    Printf = nullptr;
+  }
+};
+} // namespace options
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_OPTIONS_H_
diff --git a/compiler-rt/lib/gwp_asan/options.inc b/compiler-rt/lib/gwp_asan/options.inc
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/options.inc
@@ -0,0 +1,48 @@
+//===-- options.inc ---------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+// Please ensure that this file is kept up to date with llvm/docs/GwpAsan.rst.
+
+#ifndef GWP_ASAN_OPTION
+#error "Define GWP_ASAN_OPTION prior to including this file!"
+#endif
+
+GWP_ASAN_OPTION(bool, Enabled, true, "Is GWP-ASan enabled? Defaults to true.")
+
+GWP_ASAN_OPTION(
+    bool, PerfectlyRightAlign, false,
+    "When allocations are right-aligned, should we perfectly align them up to "
+    "the page boundary? By default (false), we round up allocation size to the "
+    "nearest power of two (2, 4, 8, 16) up to a maximum of 16-byte alignment "
+    "for performance reasons. Setting this to true can find single byte "
+    "buffer-overflows at the cost of performance, and may be incompatible with "
+    "some architectures.")
+
+GWP_ASAN_OPTION(
+    int, MaxSimultaneousAllocations, 16,
+    "Number of usable guarded slots in the allocation pool. Defaults to 16.")
+
+// Note that this is intentionally a signed long long due to sanitizer_common
+// implementation details. The __sanitizer::FlagHandler<>::Parse() calls
+// __sanitizer::internal_simple_strtoll() to do the string->integer conversion,
+// and supports a signed, 64-bit number as its maximum.
+GWP_ASAN_OPTION(
+    long long, SampleRate, 5000,
+    "The probability (1 / SampleRate) that an allocation is selected for "
+    "GWP-ASan sampling. Default is 5000. Sample rates up to 2^63 are "
+    "supported.")
+
+GWP_ASAN_OPTION(
+    bool, InstallSignalHandlers, true,
+    "Install GWP-ASan signal handlers for SIGSEGV. This allows "
+    "better error reports by providing stack traces for "
+    "allocation/deallocation when reporting a memory error. These handlers "
+    "are generally installed during dynamic loading, and will ensure that the "
+    "previously installed signal handlers will be called. User programs that "
+    "install SIGSEGV handlers should ensure that any previously installed "
+    "signal handlers are also called.")
diff --git a/compiler-rt/lib/gwp_asan/random.h b/compiler-rt/lib/gwp_asan/random.h
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/random.h
@@ -0,0 +1,28 @@
+//===-- 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 <cstdint>
+
+namespace gwp_asan {
+namespace random {
+// TODO(hctim): This may have significant overhead for platforms where
+// 64-bit arithmetic is emulated. Do we need less than a 2^32 chance of
+// sampling?
+// xorshift128+ (64-bit output). Avoids multiplication.
+uint64_t getRandomUnsigned64();
+
+// xorshift* (64-bit output). This is primarily used to seed the xorshift128+
+// generator.
+uint64_t xorShiftStar64();
+} // namespace random
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_RANDOM_H_
diff --git a/compiler-rt/lib/gwp_asan/random.cpp b/compiler-rt/lib/gwp_asan/random.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/lib/gwp_asan/random.cpp
@@ -0,0 +1,41 @@
+//===-- 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/definitions.h"
+
+#include <ctime>
+
+namespace gwp_asan {
+namespace random {
+TLS_INITIAL_EXEC uint64_t RandomStateA = static_cast<uint64_t>(time(nullptr));
+TLS_INITIAL_EXEC uint64_t RandomStateB = xorShiftStar64();
+
+uint64_t getRandomUnsigned64() {
+  uint64_t A = RandomStateA;
+  const uint64_t B = RandomStateB;
+  RandomStateA = B;
+
+  A ^= A << 23;
+  A ^= A >> 17;
+  A ^= B ^ (B >> 26);
+
+  RandomStateB = A;
+  return A + B;
+}
+
+uint64_t xorShiftStar64() {
+  uint64_t A = RandomStateA;
+  A ^= A >> 12;
+  A ^= A << 25;
+  A ^= A >> 27;
+  RandomStateA = A;
+  return A * 0x2545F4914F6CDD1D;
+}
+} // namespace random
+} // namespace gwp_asan
diff --git a/compiler-rt/lib/scudo/CMakeLists.txt b/compiler-rt/lib/scudo/CMakeLists.txt
--- a/compiler-rt/lib/scudo/CMakeLists.txt
+++ b/compiler-rt/lib/scudo/CMakeLists.txt
@@ -30,6 +30,15 @@
   RTSanitizerCommonNoTermination
   RTSanitizerCommonLibc
   RTInterception)
+
+if (GWP_ASAN_SCUDO_HOOKS)
+  # Currently, Scudo uses the GwpAsan flag parser. This backs onto the flag
+  # parsing mechanism of sanitizer_common. Once Scudo has its own flag parsing,
+  # and parses GwpAsan options, you can remove this dependency.
+  list(APPEND SCUDO_MINIMAL_OBJECT_LIBS RTGwpAsan RTGwpAsanOptionsParser)
+  list(APPEND SCUDO_CFLAGS -DGWP_ASAN_HOOKS)
+endif()
+
 set(SCUDO_OBJECT_LIBS ${SCUDO_MINIMAL_OBJECT_LIBS})
 set(SCUDO_DYNAMIC_LIBS ${SCUDO_MINIMAL_DYNAMIC_LIBS})
 
diff --git a/compiler-rt/lib/scudo/scudo_allocator.cpp b/compiler-rt/lib/scudo/scudo_allocator.cpp
--- a/compiler-rt/lib/scudo/scudo_allocator.cpp
+++ b/compiler-rt/lib/scudo/scudo_allocator.cpp
@@ -25,6 +25,11 @@
 #include "sanitizer_common/sanitizer_allocator_interface.h"
 #include "sanitizer_common/sanitizer_quarantine.h"
 
+#ifdef GWP_ASAN_HOOKS
+# include "gwp_asan/guarded_pool_allocator.h"
+# include "gwp_asan/optional/options_parser.h"
+#endif // GWP_ASAN_HOOKS
+
 #include <errno.h>
 #include <string.h>
 
@@ -213,6 +218,10 @@
   return reinterpret_cast<QuarantineCacheT *>(TSD->QuarantineCachePlaceHolder);
 }
 
+#ifdef GWP_ASAN_HOOKS
+static gwp_asan::GuardedPoolAllocator GuardedAlloc;
+#endif // GWP_ASAN_HOOKS
+
 struct Allocator {
   static const uptr MaxAllowedMallocSize =
       FIRST_32_SECOND_64(2UL << 30, 1ULL << 40);
@@ -291,6 +300,14 @@
   void *allocate(uptr Size, uptr Alignment, AllocType Type,
                  bool ForceZeroContents = false) {
     initThreadMaybe();
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.shouldSample())) {
+      if (void *Ptr = GuardedAlloc.allocate(Size))
+        return Ptr;
+    }
+#endif // GWP_ASAN_HOOKS
+
     if (UNLIKELY(Alignment > MaxAlignment)) {
       if (AllocatorMayReturnNull())
         return nullptr;
@@ -434,6 +451,14 @@
       __sanitizer_free_hook(Ptr);
     if (UNLIKELY(!Ptr))
       return;
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) {
+      GuardedAlloc.deallocate(Ptr);
+      return;
+    }
+#endif // GWP_ASAN_HOOKS
+
     if (UNLIKELY(!Chunk::isAligned(Ptr)))
       dieWithMessage("misaligned pointer when deallocating address %p\n", Ptr);
     UnpackedHeader Header;
@@ -463,6 +488,18 @@
   // size still fits in the chunk.
   void *reallocate(void *OldPtr, uptr NewSize) {
     initThreadMaybe();
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.pointerIsMine(OldPtr))) {
+      size_t OldSize = GuardedAlloc.getSize(OldPtr);
+      void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc);
+      if (NewPtr)
+        memcpy(NewPtr, OldPtr, (NewSize < OldSize) ? NewSize : OldSize);
+      GuardedAlloc.deallocate(OldPtr);
+      return NewPtr;
+    }
+#endif // GWP_ASAN_HOOKS
+
     if (UNLIKELY(!Chunk::isAligned(OldPtr)))
       dieWithMessage("misaligned address when reallocating address %p\n",
                      OldPtr);
@@ -504,6 +541,12 @@
     initThreadMaybe();
     if (UNLIKELY(!Ptr))
       return 0;
+
+#ifdef GWP_ASAN_HOOKS
+    if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr)))
+      return GuardedAlloc.getSize(Ptr);
+#endif // GWP_ASAN_HOOKS
+
     UnpackedHeader Header;
     Chunk::loadHeader(Ptr, &Header);
     // Getting the usable size of a chunk only makes sense if it's allocated.
@@ -626,6 +669,10 @@
 
 void initScudo() {
   Instance.init();
+#ifdef GWP_ASAN_HOOKS
+  gwp_asan::options::initOptions();
+  GuardedAlloc.init(*gwp_asan::options::getOptions());
+#endif // GWP_ASAN_HOOKS
 }
 
 void ScudoTSD::init() {
diff --git a/compiler-rt/test/gwp_asan/CMakeLists.txt b/compiler-rt/test/gwp_asan/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/CMakeLists.txt
@@ -0,0 +1,30 @@
+set(GWP_ASAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+set(GWP_ASAN_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+
+set(GWP_ASAN_TESTSUITES)
+
+set(GWP_ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} gwp_asan scudo)
+
+configure_lit_site_cfg(
+  ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+  ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+  )
+
+set(GWP_ASAN_TEST_ARCH ${GWP_ASAN_SUPPORTED_ARCH})
+foreach(arch ${GWP_ASAN_TEST_ARCH})
+  set(GWP_ASAN_TEST_TARGET_ARCH ${arch})
+  string(TOLOWER "-${arch}" GWP_ASAN_TEST_CONFIG_SUFFIX)
+  get_test_cc_for_arch(${arch} GWP_ASAN_TEST_TARGET_CC GWP_ASAN_TEST_TARGET_CFLAGS)
+  string(TOUPPER ${arch} ARCH_UPPER_CASE)
+  set(CONFIG_NAME ${ARCH_UPPER_CASE}${OS_NAME}Config)
+
+  configure_lit_site_cfg(
+    ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+    ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
+  list(APPEND GWP_ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+endforeach()
+
+add_lit_testsuite(check-gwp_asan "Running the GWP-ASan tests"
+  ${GWP_ASAN_TESTSUITES}
+  DEPENDS ${GWP_ASAN_TEST_DEPS})
+set_target_properties(check-gwp_asan PROPERTIES FOLDER "Compiler-RT Misc")
diff --git a/compiler-rt/test/gwp_asan/alignment_power_of_two.cpp b/compiler-rt/test/gwp_asan/alignment_power_of_two.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/alignment_power_of_two.cpp
@@ -0,0 +1,52 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=1 %run %t
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <utility>
+#include <vector>
+
+// Note, statically defined in order to avoid calling malloc() to store this
+// data. Consider this a map where element n => element n+1, and we are testing
+// that an allocation of size [n] rounds up to the nearest [n+1]-byte boundary.
+int AllocSizeToAlignment[] = {
+  1, 1,
+  2, 2,
+  3, 4,
+  4, 4,
+  5, 8,
+  7, 8,
+  8, 8,
+  9, 16,
+  15, 16,
+  16, 16,
+  17, 16,
+  31, 16,
+  32, 16,
+  33, 16,
+  4095, 4096,
+  4096, 4096
+};
+
+int main() {
+  for (unsigned i = 0; i < sizeof(AllocSizeToAlignment) / sizeof(int); i += 2) {
+    // Each allocation has a 50/50 chance of being left/right aligned. As we
+    // want to test both instances, and have no way of directly controlling
+    // left/right alignment, we test each allocation multiple times. We have a
+    // 2**-39 chance of all allocations being left or right aligned when we do
+    // 40 tests.
+    for (unsigned j = 0; j < 40; ++j) {
+      char *Ptr = reinterpret_cast<char *>(malloc(AllocSizeToAlignment[i]));
+      if (reinterpret_cast<uintptr_t>(Ptr) % AllocSizeToAlignment[i + 1] != 0) {
+        fprintf(stderr,
+                "Guarded pointer %p with size %i is not %i-byte aligned.\n",
+                Ptr, AllocSizeToAlignment[i], AllocSizeToAlignment[i + 1]);
+        exit(EXIT_FAILURE);
+      }
+      free(Ptr);
+    }
+  }
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/allocator_fallback.cpp b/compiler-rt/test/gwp_asan/allocator_fallback.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/allocator_fallback.cpp
@@ -0,0 +1,28 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=1 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=2 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=11 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=12 %run %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=13 %run %t
+
+#include <cstdlib>
+
+int main() {
+  void* Pointers[16];
+  for (unsigned i = 0; i < 16; ++i) {
+    char *Ptr = reinterpret_cast<char*>(malloc(1 << i));
+    Pointers[i] = Ptr;
+    *Ptr = 0;
+    Ptr[(1 << i) - 1] = 0;
+  }
+
+  for (unsigned i = 0; i < 16; ++i) {
+    free(Pointers[i]);
+  }
+
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/double_delete.cpp b/compiler-rt/test/gwp_asan/double_delete.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/double_delete.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Double free occurred when trying to free memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char;
+  delete Ptr;
+  delete Ptr;
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/double_deletea.cpp b/compiler-rt/test/gwp_asan/double_deletea.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/double_deletea.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Double free occurred when trying to free memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char[50];
+  delete[] Ptr;
+  delete[] Ptr;
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/double_free.cpp b/compiler-rt/test/gwp_asan/double_free.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/double_free.cpp
@@ -0,0 +1,15 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Double free occurred when trying to free memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = reinterpret_cast<char*>(malloc(10));
+  free(Ptr);
+  free(Ptr);
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp b/compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/heap_buffer_overflow.cpp
@@ -0,0 +1,17 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Buffer overflow occurred when accessing memory at:
+// CHECK: is located {{[0-9]+}} bytes to the right
+
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main() {
+  char *Ptr = reinterpret_cast<char *>(malloc(pageSize()));
+  volatile char x = *(Ptr + pageSize());
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp b/compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/heap_buffer_underflow.cpp
@@ -0,0 +1,17 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Buffer underflow occurred when accessing memory at:
+// CHECK: is located 1 bytes to the left
+
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main() {
+  char *Ptr = reinterpret_cast<char*>(malloc(pageSize()));
+  volatile char x = *(Ptr - 1);
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/invalid_free_left.cpp b/compiler-rt/test/gwp_asan/invalid_free_left.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/invalid_free_left.cpp
@@ -0,0 +1,17 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Invalid (wild) free occurred when trying to free memory at:
+// CHECK: is located 1 bytes to the left of
+
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main() {
+  char *Ptr = reinterpret_cast<char*>(malloc(pageSize()));
+  free(Ptr - 1);
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/invalid_free_right.cpp b/compiler-rt/test/gwp_asan/invalid_free_right.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/invalid_free_right.cpp
@@ -0,0 +1,17 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Invalid (wild) free occurred when trying to free memory at:
+// CHECK: is located {{[0-9]+}} bytes to the right
+
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main() {
+  char *Ptr = reinterpret_cast<char*>(malloc(pageSize()));
+  free(Ptr + pageSize());
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/lit.cfg b/compiler-rt/test/gwp_asan/lit.cfg
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/lit.cfg
@@ -0,0 +1,44 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'GWP-ASan' + config.name_suffix
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+# Test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp', '.test']
+
+# C & CXX flags.
+c_flags = ([config.target_cflags])
+
+# Android doesn't want -lrt.
+#if not config.android:
+  # c_flags += ["-lrt"]
+
+cxx_flags = (c_flags + config.cxx_mode_flags + ["-std=c++11"])
+
+gwp_asan_flags = ["-fsanitize=scudo"]
+
+def build_invocation(compile_flags):
+  return " " + " ".join([config.clang] + compile_flags) + " "
+
+# Add substitutions.
+config.substitutions.append(("%clang ", build_invocation(c_flags)))
+config.substitutions.append(("%clang_gwp_asan ", build_invocation(c_flags + gwp_asan_flags)))
+config.substitutions.append(("%clangxx_gwp_asan ", build_invocation(cxx_flags + gwp_asan_flags)))
+
+# Platform-specific default GWP_ASAN for lit tests. Ensure that GWP-ASan is
+# enabled and that it samples every allocation.
+default_gwp_asan_options = 'Enabled=1:SampleRate=1'
+
+config.environment['GWP_ASAN_OPTIONS'] = default_gwp_asan_options
+default_gwp_asan_options += ':'
+config.substitutions.append(('%env_gwp_asan_options=',
+                             'env GWP_ASAN_OPTIONS=' + default_gwp_asan_options))
+
+# GWP-ASan tests are currently supported on Linux only.
+if config.host_os not in ['Linux']:
+   config.unsupported = True
diff --git a/compiler-rt/test/gwp_asan/lit.site.cfg.in b/compiler-rt/test/gwp_asan/lit.site.cfg.in
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/lit.site.cfg.in
@@ -0,0 +1,11 @@
+@LIT_SITE_CFG_IN_HEADER@
+
+config.name_suffix = "@GWP_ASAN_TEST_CONFIG_SUFFIX@"
+config.target_arch = "@GWP_ASAN_TEST_TARGET_ARCH@"
+config.target_cflags = "@GWP_ASAN_TEST_TARGET_CFLAGS@"
+
+# Load common config for all compiler-rt lit tests.
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+
+# Load tool-specific config that would do the real work.
+lit_config.load_config(config, "@GWP_ASAN_LIT_SOURCE_DIR@/lit.cfg")
diff --git a/compiler-rt/test/gwp_asan/num_usable_slots.cpp b/compiler-rt/test/gwp_asan/num_usable_slots.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/num_usable_slots.cpp
@@ -0,0 +1,71 @@
+// REQUIRES: gwp_asan
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=1 %run %t 1 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=2 %run %t 2 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=3 %run %t 3 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=8 %run %t 8 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=16 %run %t 16 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=32 %run %t 32 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=64 %run %t 64 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=128 %run %t 128 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=129 %run %t 129 2>&1
+
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=7 not %run %t 8 2>&1
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=8 not %run %t 9 2>&1
+
+// This test massages the GPA to reveal internal data that we test for. In
+// particular, this test ensures that our allocations use all of the available
+// slots. It relies on the fact that the 1st allocation -> ${NumGuardedSlots}th
+// allocation are sequential, before PRNG selection of the guarded slots takes
+// place.
+// TODO(hctim): Update this test when the slot size is different to a single
+// page.
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main(int argc, char** argv) {
+  int NumSlots = atoi(argv[1]);
+  void **SampledAllocs =
+      static_cast<void **>(alloca(NumSlots * sizeof(void *)));
+
+  uintptr_t PageSize = pageSize();
+
+  SampledAllocs[0] = reinterpret_cast<char*>(malloc(1));
+  char *FirstGuardedSlot = reinterpret_cast<char *>(
+      reinterpret_cast<uintptr_t>(SampledAllocs[0]) & ~(PageSize - 1));
+
+  for (unsigned i = 1; i < NumSlots; ++i) {
+    char *Ptr = reinterpret_cast<char*>(malloc(PageSize));
+    char *Slot = reinterpret_cast<char *>(reinterpret_cast<uintptr_t>(Ptr) &
+                                          ~(PageSize - 1));
+    if (Slot != FirstGuardedSlot + (i * 2 * PageSize)) {
+      fprintf(
+          stderr,
+          "Allocation number %i (%p) was either not in the guarded pool, or it "
+          "is reusing a slot before all slots have been used once.\n",
+          i, Ptr);
+      exit(EXIT_FAILURE);
+    }
+    SampledAllocs[i] = Ptr;
+  }
+
+  // Now, we have exhausted the pool. Check to see that the next allocation goes
+  // to the system allocator.
+  char *Ptr = reinterpret_cast<char*>(malloc(PageSize));
+  // The system allocator doesn't have use-after-free READ detection. If this
+  // was a sampled allocation, this would be caught.
+  free(Ptr);
+  volatile char x = *Ptr;
+
+  // Clean up allocations.
+  for (unsigned i = 0; i < NumSlots; ++i) {
+    free(SampledAllocs[i]);
+  }
+
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/page_size.h b/compiler-rt/test/gwp_asan/page_size.h
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/page_size.h
@@ -0,0 +1,11 @@
+#ifndef PAGE_SIZE_
+#define PAGE_SIZE_
+
+#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
+# include <unistd.h>
+unsigned pageSize() {
+  return sysconf(_SC_PAGESIZE);
+}
+#endif
+
+#endif // PAGE_SIZE_
diff --git a/compiler-rt/test/gwp_asan/pattern_calloc_free.cpp b/compiler-rt/test/gwp_asan/pattern_calloc_free.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/pattern_calloc_free.cpp
@@ -0,0 +1,21 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %run %t
+
+#include <cstdlib>
+
+int main() {
+  for (unsigned i = 1; i < 0x100000; i <<= 1) {
+    char *Ptr = reinterpret_cast<char *>(calloc(i, sizeof(char)));
+
+    for (unsigned j = 0; j < i; ++j) {
+      *(Ptr + j) = 0x0;
+    }
+
+    free(Ptr);
+  }
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/pattern_malloc_free.cpp b/compiler-rt/test/gwp_asan/pattern_malloc_free.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/pattern_malloc_free.cpp
@@ -0,0 +1,21 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %run %t
+
+#include <cstdlib>
+
+int main() {
+  for (unsigned i = 1; i < 0x100000; i <<= 1) {
+    char *Ptr = reinterpret_cast<char *>(malloc(i));
+
+    for (unsigned j = 0; j < i; ++j) {
+      *(Ptr + j) = 0x0;
+    }
+
+    free(Ptr);
+  }
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/pattern_new_delete.cpp b/compiler-rt/test/gwp_asan/pattern_new_delete.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/pattern_new_delete.cpp
@@ -0,0 +1,13 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %run %t
+
+int main() {
+  char *Ptr = new char;
+  *Ptr = 0x0;
+  delete Ptr;
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/pattern_newa_deletea.cpp b/compiler-rt/test/gwp_asan/pattern_newa_deletea.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/pattern_newa_deletea.cpp
@@ -0,0 +1,19 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %run %t
+
+int main() {
+  for (unsigned i = 1; i < 0x100000; i <<= 1) {
+    char *Ptr = new char[i];
+
+    for (unsigned j = 0; j < i; ++j) {
+      *(Ptr + j) = 0x0;
+    }
+
+    delete[] Ptr;
+  }
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/pattern_realloc_free.cpp b/compiler-rt/test/gwp_asan/pattern_realloc_free.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/pattern_realloc_free.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %run %t
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = reinterpret_cast<char *>(realloc(nullptr, 1));
+  for (unsigned i = 1; i < 0x100000; i <<= 1) {
+    Ptr = reinterpret_cast<char *>(realloc(Ptr, i));
+
+    for (unsigned j = 0; j < i; ++j) {
+      *(Ptr + j) = 0x0;
+    }
+  }
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/reuse_quarantine.cpp b/compiler-rt/test/gwp_asan/reuse_quarantine.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/reuse_quarantine.cpp
@@ -0,0 +1,35 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation/memory access/deallocation works
+// as expected.
+
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=1 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=2 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=8 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=16 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=32 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=64 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=128 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=129 not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Buffer overflow occurred when accessing memory at:
+// CHECK: is located {{[0-9]+}} bytes to the right of a
+
+#include <cstdlib>
+
+#include "page_size.h"
+
+int main() {
+  unsigned PageSize = pageSize();
+  for (unsigned i = 0; i < 128; ++i) {
+    char *Ptr = reinterpret_cast<char*>(malloc(PageSize));
+    *Ptr = 0x0;
+    free(Ptr);
+  }
+
+  char *Ptr = reinterpret_cast<char*>(malloc(PageSize));
+  volatile char x = *(Ptr + PageSize);
+
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/thread_contention.cpp b/compiler-rt/test/gwp_asan/thread_contention.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/thread_contention.cpp
@@ -0,0 +1,71 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation works as expected under threading
+// conditions.
+
+// Note: Compilation of <atomic> and <thread> are extremely expensive for
+// non-opt builds of clang. We attempt to minimise this by making a single
+// compilation and everything else is decided at runtime.
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=1024 %t 1024
+
+// Intentionally allocate and free all slots before thrashing. This means we
+// get the random slot selection mechanism.
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=1024 %t 1024 RNG
+
+#include <atomic>
+#include <cstdlib>
+#include <thread>
+
+#include "page_size.h"
+
+void asyncMalloc(void *Pointers[], int NumAllocs,
+                 std::atomic<bool> *StartingGun) {
+  while (!StartingGun->load()) {
+  }
+
+  for (int i = 0; i < NumAllocs; ++i)
+    Pointers[i] = malloc(pageSize());
+}
+
+int main(int argc, char **argv) {
+  if (argc < 2)
+    exit(EXIT_FAILURE);
+  int NumAllocsTotal = atoi(argv[1]);
+  int NumAllocsForThread2 = NumAllocsTotal - (NumAllocsTotal / 2);
+
+  // Optionally, allocate and free all the possible allocations so that PRNG
+  // selection of the slot takes place.
+  if (argc > 2) {
+    for (int i = 0; i < NumAllocsTotal; ++i) {
+      void *Ptr = malloc(1);
+      free(Ptr);
+    }
+  }
+
+  // Each thread gets half of this as a thread-local section to save into.
+  void **Pointers = new void *[NumAllocsTotal];
+
+  std::atomic<bool> StartingGun{false};
+
+  std::thread T1(asyncMalloc, Pointers, NumAllocsTotal / 2, &StartingGun);
+  std::thread T2(asyncMalloc, &Pointers[NumAllocsTotal / 2],
+                 NumAllocsForThread2, &StartingGun);
+
+  StartingGun.store(true);
+
+  T1.join();
+  T2.join();
+
+  for (unsigned i = 0; i < NumAllocsTotal; ++i) {
+    for (unsigned j = i + 1; j < NumAllocsTotal; ++j) {
+      if (Pointers[i] == Pointers[j]) {
+        // Pointer was dual-allocated.
+        exit(EXIT_FAILURE);
+      }
+    }
+    free(Pointers[i]);
+  }
+  delete[] Pointers;
+
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/thread_contention_with_release.cpp b/compiler-rt/test/gwp_asan/thread_contention_with_release.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/thread_contention_with_release.cpp
@@ -0,0 +1,103 @@
+// REQUIRES: gwp_asan
+// This test ensures that normal allocation and deallocation works as expected
+// under threading conditions.
+
+// Note: Compilation of <atomic> and <thread> are extremely expensive for
+// non-opt builds of clang. We attempt to minimise this by making a single
+// compilation and everything else is decided at runtime.
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=2048 %t 2048
+
+// Intentionally allocate and free all slots before thrashing. This means we
+// get the random slot selection mechanism.
+// RUN: %env_gwp_asan_options=MaxSimultaneousAllocations=2048 %t 2048 RNG
+
+#include <atomic>
+#include <chrono>
+#include <cstdlib>
+#include <thread>
+#include <vector>
+
+#include "page_size.h"
+
+const int MAX_THREADS_TO_USE = 8;
+
+void task(int NumTests, std::atomic<bool> *StartingGun) {
+  while (!StartingGun->load()) {
+  }
+
+  // Get ourselves a new allocation.
+  for (int i = 0; i < NumTests; ++i) {
+    volatile char *Ptr = reinterpret_cast<volatile char *>(malloc(pageSize()));
+    // Do any other threads have access to this page?
+    if (*Ptr != 0)
+      exit(EXIT_FAILURE);
+
+    // Mark the page as from malloc. Wait to see if another thread also takes
+    // this page.
+    *Ptr = 'A';
+    std::this_thread::sleep_for(std::chrono::nanoseconds(10000));
+
+    // Check we still own the page.
+    if (*Ptr != 'A')
+      exit(EXIT_FAILURE);
+
+    // Set the page as unowned, and release it back to the pool using realloc.
+    *Ptr = 0;
+    Ptr = reinterpret_cast<volatile char *>(
+        realloc(const_cast<char *>(Ptr), pageSize() - 1));
+    // Check the new pointer is unowned.
+    if (*Ptr != 0)
+      exit(EXIT_FAILURE);
+
+    // Set the pointer as owned by realloc and wait to see if another thread
+    // takes it.
+    *Ptr = 'B';
+    std::this_thread::sleep_for(std::chrono::nanoseconds(10000));
+
+    // Check we still own it.
+    if (*Ptr != 'B')
+      exit(EXIT_FAILURE);
+
+    // And now release it.
+    *Ptr = 0;
+    free(const_cast<char *>(Ptr));
+  }
+}
+
+int main(int argc, char **argv) {
+  if (argc < 2)
+    exit(EXIT_FAILURE);
+  int NumGuardedSlots = atoi(argv[1]);
+  // Ensure that we have enough slots for the STL allocations, primarily the
+  // std::set.
+  int NumAllocsTotal = NumGuardedSlots / 2;
+
+  // Optionally, allocate and free all the possible allocations so that PRNG
+  // selection of the slot takes place.
+  if (argc > 2) {
+    for (int i = 0; i < NumGuardedSlots; ++i) {
+      void *Ptr = malloc(1);
+      free(Ptr);
+    }
+  }
+
+  std::atomic<bool> StartingGun{false};
+  std::vector<std::thread> Threads;
+
+  int NumThreads = MAX_THREADS_TO_USE;
+  if (std::thread::hardware_concurrency() < MAX_THREADS_TO_USE) {
+    NumThreads = std::thread::hardware_concurrency();
+  }
+
+  for (int i = 0; i < NumThreads; ++i) {
+    Threads.emplace_back(task, NumAllocsTotal / NumThreads, &StartingGun);
+  }
+
+  StartingGun.store(true);
+
+  for (auto &T : Threads)
+    T.join();
+
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/use_after_delete.cpp b/compiler-rt/test/gwp_asan/use_after_delete.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/use_after_delete.cpp
@@ -0,0 +1,18 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Use after free occurred when accessing memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char;
+
+  *Ptr = 0x0;
+
+  delete Ptr;
+  volatile char x = *Ptr;
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/use_after_deletea.cpp b/compiler-rt/test/gwp_asan/use_after_deletea.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/use_after_deletea.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Use after free occurred when accessing memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = new char[10];
+
+  for (unsigned i = 0; i < 10; ++i) {
+    *(Ptr + i) = 0x0;
+  }
+
+  delete[] Ptr;
+  volatile char x = *Ptr;
+  return 0;
+}
diff --git a/compiler-rt/test/gwp_asan/use_after_free.cpp b/compiler-rt/test/gwp_asan/use_after_free.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/gwp_asan/use_after_free.cpp
@@ -0,0 +1,20 @@
+// REQUIRES: gwp_asan
+// RUN: %clangxx_gwp_asan %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// CHECK: GWP-ASan detected a memory error
+// CHECK: Use after free occurred when accessing memory at:
+
+#include <cstdlib>
+
+int main() {
+  char *Ptr = reinterpret_cast<char*>(malloc(10));
+
+  for (unsigned i = 0; i < 10; ++i) {
+    *(Ptr + i) = 0x0;
+  }
+
+  free(Ptr);
+  volatile char x = *Ptr;
+  return 0;
+}
diff --git a/compiler-rt/test/lit.common.cfg b/compiler-rt/test/lit.common.cfg
--- a/compiler-rt/test/lit.common.cfg
+++ b/compiler-rt/test/lit.common.cfg
@@ -239,6 +239,9 @@
 if config.can_symbolize:
   config.available_features.add('can-symbolize')
 
+if config.gwp_asan_scudo_hooks:
+  config.available_features.add('gwp_asan')
+
 lit.util.usePlatformSdkOnDarwin(config, lit_config)
 
 if config.host_os == 'Darwin':
diff --git a/compiler-rt/test/lit.common.configured.in b/compiler-rt/test/lit.common.configured.in
--- a/compiler-rt/test/lit.common.configured.in
+++ b/compiler-rt/test/lit.common.configured.in
@@ -41,6 +41,7 @@
 set_default("android_serial", "@ANDROID_SERIAL_FOR_TESTING@")
 set_default("android_files_to_push", [])
 set_default("have_rpc_xdr_h", @HAVE_RPC_XDR_H@)
+set_default("gwp_asan_scudo_hooks", @GWP_ASAN_SCUDO_HOOKS_PYBOOL@)
 config.available_features.add('target-is-%s' % config.target_arch)
 
 if config.enable_per_target_runtime_dir:
diff --git a/compiler-rt/test/scudo/lit.cfg b/compiler-rt/test/scudo/lit.cfg
--- a/compiler-rt/test/scudo/lit.cfg
+++ b/compiler-rt/test/scudo/lit.cfg
@@ -49,6 +49,12 @@
   # Android defaults to abort_on_error=1, which doesn't work for us.
   default_scudo_opts = 'abort_on_error=0'
 
+# Disable GWP-ASan for scudo internal tests.
+gwp_asan_options = 'Enabled=0'
+config.environment['GWP_ASAN_OPTIONS'] = gwp_asan_options
+config.substitutions.append(('%env_gwp_asan_options=',
+                             'env GWP_ASAN_OPTIONS=' + gwp_asan_options))
+
 if default_scudo_opts:
   config.environment['SCUDO_OPTIONS'] = default_scudo_opts
   default_scudo_opts += ':'
diff --git a/llvm/docs/GwpAsan.rst b/llvm/docs/GwpAsan.rst
new file mode 100644
--- /dev/null
+++ b/llvm/docs/GwpAsan.rst
@@ -0,0 +1,187 @@
+========
+GWP-ASan
+========
+
+.. contents::
+   :local:
+   :depth: 2
+
+Introduction
+============
+
+GWP-ASan is a sampled allocator framework that assists in finding use-after-free
+and heap-buffer-overflow bugs in production environments. It informally is a
+recursive acronym, "**G**\WP-ASan **W**\ill **P**\rovide **A**\llocation
+**SAN**\ity".
+
+GWP-ASan is based on the classic
+`Electric Fence Malloc Debugger <https://linux.die.net/man/3/efence>`_, with a
+key adaptation. Notably, we only choose a very small percentage of allocations
+to sample, and apply guard pages to these sampled allocations only. The sampling
+is small enough to allow us to have very low performance overhead.
+
+There is a small, tunable memory overhead that is fixed for the lifetime of the
+process. This is approximately ~60KiB per process using the default settings,
+depending on the average size of your allocations. Future improvements should
+drastically reduce this amount.
+
+GWP-ASan vs. ASan
+=================
+
+Unlike `AddressSanitizer <https://clang.llvm.org/docs/AddressSanitizer.html>`_,
+GWP-ASan does not induce a significant performance overhead. ASan often requires
+the use of dedicated canaries to be viable in production environments, and as
+such is often impractical.
+
+GWP-ASan is only capable of finding a subset of the memory issues detected by
+ASan. Furthermore, GWP-ASan's bug detection capabilities are only probabilistic.
+As such, we recommend using ASan over GWP-ASan in testing, as well as anywhere
+else that guaranteed error detection is more valuable than the 2x execution
+slowdown/binary size bloat. For the majority of production environments, this
+impact is too high, and GWP-ASan proves extremely useful.
+
+Design
+======
+
+**Please note:** The implementation of GWP-ASan is largely in-flux, and these
+details are subject to change. There are currently other implementations of
+GWP-ASan, such as the implementation featured in
+`Chromium <https://cs.chromium.org/chromium/src/components/gwp_asan/>`_. The
+long-term support goal is to ensure feature-parity where reasonble, and to
+support compiler-rt as the reference implementation.
+
+Allocator Support
+-----------------
+
+GWP-ASan is not a replacement for a traditional allocator. Instead, it works by
+inserting stubs into an existing allocator to redirect allocations when they're
+chosen to be sampled. These stubs are generally implemented in the implementaion
+of ``malloc()``, ``free()`` and ``realloc()``. The stubs are extremely small,
+which makes using GWP-ASan in most allocators fairly trivial. The stubs follow
+the same general pattern (example ``malloc()`` pseudocode below):
+
+.. code:: cpp
+  #ifdef INSTALL_GWP_ASAN_STUBS
+    gwp_asan::GuardedPoolAllocator GWPASanAllocator;
+  #endif
+  
+  void* YourAllocator::malloc(..) {
+  #ifdef INSTALL_GWP_ASAN_STUBS
+    if (GWPASanAllocator.shouldSample(..))
+      return GWPASanAllocator.allocate(..);
+  #endif
+  
+    // ... the rest of your allocator code here.
+  }
+
+Then, all the supported allocator needs to do is compile with
+``-DINSTALL_GWP_ASAN_STUBS`` and link against the GWP-ASan library!
+
+Guarded Allocation Pool
+-----------------------
+
+The core of GWP-ASan is the guarded allocation pool. Each sampled allocation is
+backed using its own *guarded* slot, which may consist of one or more accessible
+pages. Each guarded slot is surrounded by two *guard* pages, which are mapped as
+inaccessible. We create a contiguous buffer of this ``guard_page | guarded_slot
+| guard_page`` pattern, which we call the *guarded allocation pool*.
+
+Buffer Underflow/Overflow Detection
+-----------------------------------
+
+We gain buffer-overflow and buffer-underflow through these guard pages. When a
+memory access overruns the allocated buffer, it will touch the inaccessible
+guard page, causing memory exception. This exception is caught and handled by
+the internal crash handler. Because each allocation is recorded with metadata
+about where (and by what thread) it was allocated and deallocated, we can
+provide helpful information that will help identify the root cause of the bug.
+
+In order to increase our detection of overflows, we randomly align half of the
+allocations to the right hand side of the guarded slot.
+
+Use after Free Detection
+------------------------
+
+The guarded allocation pool also provide use-after-free detection. Whenever a
+sampled allocation is deallocated, we map the guard page as inaccessible. Any
+memory accesses after deallocation will thus trigger the crash handler, and we
+can provide useful information about the source of the error.
+
+Please note that the use-after-free detection for a sampled allocation is
+transient. We reuse inaccessible slots on-demand as we have a fixed number of
+them, and wish to avoid starving the allocation pool to solely catch
+use-after-frees. We ranomly choose an inaccessible slot to reuse in order to
+provide a chance of detecting long-lived use-after-frees.
+
+Usage
+=====
+
+Currently, the only allocator that supports GWP-ASan is the
+`Scudo Hardened Allocator <https://llvm.org/docs/ScudoHardenedAllocator.html>`_.
+Building compiler-rt will install GWP-ASan into your local version of Scudo, and
+any binary built with Scudo will also have GWP-ASan enabled by default.
+Instructions on using Scudo for your applications can be found on the Scudo wiki
+page.
+
+Options
+-------
+
+GWP-ASan is configured on a per-allocator basis. We provide a default
+implementation of configuration that is used by Scudo. Several aspects of
+GWP-ASan can be configured on a per process basis through the following ways:
+
+- at compile time, by defining ``GWP_ASAN_DEFAULT_OPTIONS`` to the options
+  string you want set by default;
+
+- by defining a ``__gwp_asan_default_options`` function in one's program that
+  returns the options string to be parsed. Said function must have the following
+  prototype: ``extern "C" const char* __gwp_asan_default_options(void)``, with a
+  default visibility. This will override the compile time define;
+
+- through the environment variable ``GWP_ASAN_OPTIONS``, containing the options string
+  to be parsed. Options defined this way will override any definition made
+  through ``__gwp_asan_default_options``.
+
+The options string follows a syntax similar to ASan, where distinct options
+can be assigned in the same string, separated by colons.
+
+For example, using the environment variable:
+
+.. code:: console
+
+  GWP_ASAN_OPTIONS="NumUsableGuardedPages=16:SampleRate=5000" ./a.out
+
+Or using the function:
+
+.. code:: cpp
+
+  extern "C" const char *__gwp_asan_default_options() {
+    return "NumUsableGuardedPages=16:SampleRate=5000";
+  }
+
+The following options are available:
+
++----------------------------+--------------------+---------------------------------------------------------------------------------+
+| Option                     |  Default           |  Description                                                                    |
++----------------------------+--------------------+---------------------------------------------------------------------------------+
+| Enabled                    |  true              |  Is GWP-ASan enabled?                                                           |
++----------------------------+--------------------+---------------------------------------------------------------------------------+
+| PerfectlyRightAlign        |  false             |  When allocations are right-aligned, should we perfectly align them up to the   |
+|                            |                    |  page boundary? By default (false), we round up allocation size to the nearest  |
+|                            |                    |  power of two (2, 4, 8, 16) up to a maximum of 16-byte alignment for            |
+|                            |                    |  performance reasons. Setting this to true can find single byte                 |
+|                            |                    |  buffer-overflows at the cost of performance, and may be incompatible with      |
+|                            |                    |  some architectures.                                                            |
++----------------------------+--------------------+---------------------------------------------------------------------------------+
+| MaxSimultaneousAllocations |  16                |  Number of simultaneously-guarded allocations available in the pool.            |
++----------------------------+--------------------+---------------------------------------------------------------------------------+
+| SampleRate                 |  5000              |  The probability (1 / SampleRate) that a page is selected for GWP-ASan          |
+|                            |                    |  sampling. Sample rates up to 2^63 are supported.                               |
++----------------------------+--------------------+---------------------------------------------------------------------------------+
+| InstallSignalHandlers      |  true              |  Install GWP-ASan signal handlers for SIGSEGV. This allows better error reports |
+|                            |                    |  by providing stack traces for allocation/deallocation when reporting a         |
+|                            |                    |  memory error. These handlers are generally installed during dynamic loading,   |
+|                            |                    |  and will ensure that the previously installed signal handlers will be called.  |
+|                            |                    |  User programs that install SIGSEGV handlers should ensure that any previously  |
+|                            |                    |  installed signal handlers are also called.                                     |
++----------------------------+--------------------+---------------------------------------------------------------------------------+